Compare commits

...

153 Commits

Author SHA1 Message Date
Luca Frosini 95cdb46f00 Removed -SNAPSHOT to release the component 2024-05-14 15:42:27 +02:00
Luca Frosini 75104813dc Updated maven parent 2024-05-14 14:20:42 +02:00
Luca Frosini 77209f01cf Moved tests in catalogue-core 2024-05-13 18:10:53 +02:00
Luca Frosini 9c087550d2 Improved changelog 2024-05-02 11:36:33 +02:00
Luca Frosini 638823062a Fixed typos and grammar 2024-04-16 14:48:47 +02:00
Luca Frosini 322a9509fb Added new version of base class for tests 2024-03-27 14:54:41 +01:00
Luca Frosini 07fb1e4213 ServiceCatalogueConfiguration class provided as argument 2024-03-27 12:00:25 +01:00
Luca Frosini 3c8932cd1f Moving core code to dedicated library 2024-03-25 19:48:05 +01:00
Luca Frosini 7cebe2beb5 Improved profiles schemas 2024-01-23 17:15:20 +01:00
Luca Frosini 3c05b571c9 Removed -SNAPSHOT for release 2024-01-22 12:21:10 +01:00
Luca Frosini a13cb0ed8f Implementing feature #26142 2024-01-19 15:15:19 +01:00
Luca Frosini 51731bbd04 adding feature #26142 2024-01-19 14:52:18 +01:00
Luca Frosini c17bf6de9a Removed -SNAPSHOT to release the component 2024-01-18 16:40:10 +01:00
Luca Frosini e47b147301 Added -SNAPSHOT to allow jenkins to build it 2024-01-18 16:20:49 +01:00
Luca Frosini 173100f123 Removed -SNAPSHOT to release the component 2024-01-18 12:39:19 +01:00
Luca Frosini 75353a57d1 Fixed changelog 2024-01-18 12:28:54 +01:00
Luca Frosini 7915074947 Enforcing private item to avoid issue #26391 2024-01-18 12:27:36 +01:00
Luca Frosini 703b284b92 Fixed query to properly support IS model changes refs #24992 2024-01-17 14:53:10 +01:00
luca.frosini 41e88f0c6e Fixed deprecated function 2023-09-12 16:00:36 +02:00
luca.frosini 50d1974424 Fixed changelog 2023-09-11 11:06:08 +02:00
luca.frosini 3d055d7168 Notification is now sent as separated thread 2023-09-11 11:03:08 +02:00
luca.frosini 017dff0630 Removed -SNAPSHOT to release the component 2023-09-08 15:49:15 +02:00
luca.frosini e9450c6a95 Fixed bom for SNAPSHOT 2023-09-07 16:44:46 +02:00
luca.frosini c5e05d3495 Restored -SNAPSHOT to deploy the service in dev 2023-09-07 16:42:10 +02:00
luca.frosini 9a5f755c75 Fixed naming 2023-09-05 15:57:39 +02:00
luca.frosini edcf71deba Removed -SNAPSHOT for release 2023-07-21 14:39:32 +02:00
luca.frosini 47f181ea76 Enhanced lower bound of range of storagehub-application-persistence 2023-07-20 17:15:36 +02:00
luca.frosini 04bdf3bff5 Reorganized service 2023-07-18 16:04:16 +02:00
luca.frosini ded9f23deb Reorganized code 2023-07-14 16:24:38 +02:00
luca.frosini 0cf6e58dd4 Removed GRSF utilities moved in the dedicated component 2023-07-14 10:59:33 +02:00
luca.frosini 1913af6663 Reorganizing code 2023-07-13 10:48:33 +02:00
luca.frosini e766c3bb92 improved service 2023-07-11 15:27:44 +02:00
luca.frosini 3bd109e993 Added comment on workaround 2023-07-11 12:13:33 +02:00
luca.frosini d5137f8a82 Looking for a workaround for a storagehub issue 2023-07-11 11:47:27 +02:00
luca.frosini 69fb4eafa0 Fixing secret sessions 2023-07-11 11:09:04 +02:00
luca.frosini 3bfbe5ed9b Added session in all methods 2023-07-11 10:55:53 +02:00
luca.frosini 1199570e57 Fixing token generation for clientId 2023-07-11 10:25:13 +02:00
luca.frosini 79b6527057 Fixing initialization 2023-07-11 09:59:35 +02:00
luca.frosini 080255e4ec Adding syncronization 2023-07-11 09:32:42 +02:00
luca.frosini 89d3604886 Fixed range 2023-07-10 16:59:00 +02:00
luca.frosini 2eef938c71 Fixing code 2023-07-10 16:42:11 +02:00
luca.frosini ca07b2f9f1 clientId is now got from properties 2023-07-10 16:14:54 +02:00
luca.frosini e5cf080bcd formatted code 2023-07-04 15:33:36 +02:00
luca.frosini 5426e171ad fixing test 2023-07-03 11:23:54 +02:00
luca.frosini 0f1d3e544b Speedup URIResolver 2023-06-28 17:23:23 +02:00
luca.frosini b189ad0e72 Fixed code 2023-06-27 15:02:19 +02:00
luca.frosini e378d275bc Fixing organization filtering 2023-06-27 14:58:11 +02:00
luca.frosini d132dfad92 Fixing org filtering 2023-06-27 14:56:58 +02:00
luca.frosini 657802918a Trying to fix multiple organization listing 2023-06-27 14:37:32 +02:00
luca.frosini 4c900e6864 Fixed classLoader 2023-06-26 16:03:57 +02:00
luca.frosini 91c25084cd Restoring working classLaoder to get Resource 2023-06-26 15:59:03 +02:00
luca.frosini 72742086fd Fixed classLoader to get a Resource 2023-06-26 11:24:21 +02:00
luca.frosini c29377bc6d Fixing property reading 2023-06-26 10:43:41 +02:00
luca.frosini ce8f089de8 Porting naming to allow usage of gcat from other services as library 2023-06-23 18:01:12 +02:00
luca.frosini 5194e734aa Improving catalogue configuration management 2023-06-23 17:25:20 +02:00
luca.frosini 117905ff7e Fixing code 2023-06-22 15:22:58 +02:00
luca.frosini 2bfadb3a08 Ignored MacOs File 2023-06-21 11:31:51 +02:00
Luca Frosini 88196d4ca7 Ordered users alphabetically 2023-06-10 15:16:29 +02:00
Luca Frosini 1082e60f81 Supporting read of GRSF records 2023-05-31 16:35:12 +02:00
Luca Frosini 43b8fbbacd Fixed query template 2023-05-15 14:34:49 +02:00
Luca Frosini cdbd2d2fb7 Fixed changelog 2023-05-04 15:51:31 +02:00
Luca Frosini 6dfa3da0db Merge remote-tracking branch 'origin/new_schema' 2023-05-04 15:36:41 +02:00
Luca Frosini 2b3cf622b0 Migrated code to new is-model 2023-05-04 15:10:58 +02:00
Luca Frosini 08e7fd0ea9 added example profile 2023-04-13 11:15:13 +02:00
Luca Frosini 5d7d0d26d2 Upgraded gcube-smartgears-bom 2023-04-06 09:47:23 +02:00
Luca Frosini e046ee68b0 Fixed md file 2023-04-05 15:39:11 +02:00
Luca Frosini e4508f779d Fixed CHANGELOG 2023-04-05 15:08:30 +02:00
Luca Frosini c919033843 adding br 2023-04-05 14:54:22 +02:00
Luca Frosini 1597ddcfd6 adding markdown doc version 2023-04-05 14:52:07 +02:00
Luca Frosini bfc83b99ba Improved profile schema version 5 2023-03-21 11:38:17 +01:00
Luca Frosini 1f18f1f95f Enhanced profile specification 2023-03-20 10:19:40 +01:00
Luca Frosini b6977f52a0 Removed unneeded imports 2023-03-17 19:01:03 +01:00
Luca Frosini c9ef3a438a Extracted Vocabulary definition as type 2023-03-17 19:00:38 +01:00
Luca Frosini dbd7d51d1b Created new version of the schema 2023-03-14 11:58:52 +01:00
Luca Frosini 9f14bb3b62 Fixed init log 2023-03-09 10:10:24 +01:00
Luca Frosini 628149bf28 Removed -SNAPSHOT to release the component 2023-03-03 12:20:01 +01:00
Luca Frosini 4edbf54eb2 Added author information to item in case of moderation 2023-03-01 13:55:50 +01:00
Luca Frosini 72a2233dbe Fixed ckan user retrieving 2023-03-01 12:23:38 +01:00
Luca Frosini f117096847 Fixing moderator issues 2023-02-28 17:06:47 +01:00
Luca Frosini 29202bf152 Moved JSON:API to single function to control default 2023-02-28 15:51:12 +01:00
Luca Frosini 53be7fcd10 Fixing default content-type 2023-02-28 15:44:25 +01:00
Luca Frosini 4b54aa3103 Removed usage of Encrypted Property Type and added Vault instead 2023-02-27 15:31:50 +01:00
Luca Frosini f7152f0be1 Removed dependency version declaration which is taken from bom 2023-02-24 09:45:01 +01:00
Luca Frosini 72842ff53b Added support for JSON:API to item list method 2023-02-23 15:59:44 +01:00
Luca Frosini 39de19d109 Updated gcat-api range 2023-02-23 12:25:22 +01:00
Luca Frosini 8bfb0c3b39 Improved code efficiency 2023-02-17 17:45:02 +01:00
Luca Frosini 2d9f469a46 Moving JSON:API reply separate methods 2023-02-17 17:39:38 +01:00
Luca Frosini cbcb4804b7 Adding new version on export file 2023-02-17 15:21:23 +01:00
Luca Frosini f2cb73c563 improving solution 2023-02-17 14:34:08 +01:00
Luca Frosini 7453f3be78 Adding support for JSON:API for licenses 2023-02-17 14:31:46 +01:00
Luca Frosini 0a79e2d769 Adding support for JSON:API 2023-02-17 14:21:32 +01:00
Luca Frosini e5adc1e3a7 Fixed imports 2023-02-17 12:29:49 +01:00
Luca Frosini 96eb118dba Fixed gcube-smartgears-bom to comply with new distribution 2023-02-17 12:18:58 +01:00
Luca Frosini 434d42a1d5 Improved code 2023-02-17 12:10:23 +01:00
Luca Frosini 80bee8029b dialing with FacetBasedISConfigurationProxy failing 2023-02-17 11:53:45 +01:00
Luca Frosini f3e1c29aef Added delete propagation constraint 2023-02-02 21:19:42 +01:00
Luca Frosini ad2c722fb2 Updated documentation 2023-02-01 17:45:44 +01:00
Luca Frosini e30794baa6 fixed bug 2023-02-01 17:35:07 +01:00
Luca Frosini 959b350674 message reduced refs #23575 2023-02-01 17:11:22 +01:00
Luca Frosini 7d14ca0496 Fixed notification 2023-02-01 16:27:45 +01:00
Luca Frosini cb8c38b07b Fixed changelog 2023-02-01 16:05:40 +01:00
Luca Frosini 56c8f672a9 Aligned doc with new features/bug fixes 2023-02-01 16:01:36 +01:00
Luca Frosini 2cc08d4b25 Added missing notification 2023-02-01 15:48:41 +01:00
Luca Frosini a273dd3f8b Fixed changelog 2023-02-01 09:53:03 +01:00
Luca Frosini b01fdcb81c Merge remote-tracking branch 'origin/feature/20225' 2023-02-01 09:51:29 +01:00
Luca Frosini 551db893a3 Fixing zenodo export file 2023-01-30 18:30:16 +01:00
Luca Frosini e31405850a Fixed code 2023-01-26 10:42:50 +01:00
Luca Frosini 2f6cd311ba Merge branch 'feature/20225' of gitea@code-repo.d4science.org:gCubeSystem/gcat.git into feature/20225 2023-01-26 10:34:07 +01:00
Luca Frosini b2b2306c37 commented no more needed code 2023-01-26 10:33:27 +01:00
Luca Frosini 180e043e65 Any user can remove self from cache 2023-01-26 10:31:46 +01:00
Luca Frosini 4e015d5a44 Any user can remove self from cache 2023-01-26 10:30:22 +01:00
Luca Frosini 45cc8fcae9 Updated conf.py 2023-01-25 23:41:01 +01:00
Luca Frosini b48a497c30 Fixed changelog and added json to publish/export software 2023-01-16 19:16:01 +01:00
Luca Frosini 5c2041df67 Fixed Request Filter 2023-01-13 15:15:16 +01:00
Luca Frosini 0ba536843d Fixed changelog and readme 2023-01-13 14:06:18 +01:00
Luca Frosini 770061cf9f Fixed Changelog 2023-01-02 11:59:04 +01:00
Luca Frosini da5649ec6f Fixed changelog dates 2022-12-23 11:00:35 +01:00
Luca Frosini 95f6e3940b Merge branch 'feature/20225' of
gitea@code-repo.d4science.org:gCubeSystem/gcat.git into feature/20225
2022-12-22 15:25:03 +01:00
Luca Frosini 1971d5f0de Merge branch 'feature/20225' of gitea@code-repo.d4science.org:gCubeSystem/gcat.git into feature/20225 2022-12-22 14:54:29 +01:00
Luca Frosini fca67f8cb5 Removed funding from readme and moved to the dedicated file 2022-12-22 12:27:58 +01:00
Luca Frosini e5554f3935 Fixed Changelog 2022-12-21 14:11:38 +01:00
Luca Frosini 4ef0cb2112 improving gcat documentation 2022-12-15 13:01:06 +01:00
Luca Frosini 9fa254add0 improved logging 2022-12-15 11:35:58 +01:00
Luca Frosini c68771669e Improved docs 2022-12-14 17:55:57 +01:00
Luca Frosini 7e220d7f77 Fixed documentation typo 2022-12-14 15:18:21 +01:00
Luca Frosini 98c7a3c59c Fixed code 2022-12-13 12:35:08 +01:00
Luca Frosini 6df1eed2c2 QueryTemplate is now created only one time 2022-12-13 12:34:23 +01:00
Luca Frosini 662f3a90f2 Removed web.xml 2022-12-13 11:52:41 +01:00
Luca Frosini 94ebf3830c Adding web.xml to properly manage GCatInitializator 2022-12-13 11:46:43 +01:00
Luca Frosini ea3c484c31 Upgraded version 2022-12-13 11:29:12 +01:00
Luca Frosini 92caa0377a Added comment as reminder 2022-12-13 10:54:50 +01:00
Luca Frosini 72b2be6010 Fixed documentation 2022-12-13 10:34:51 +01:00
Luca Frosini 161977ae00 Improved code and test 2022-12-07 23:21:02 +01:00
Luca Frosini 2cc004df4c Implementing Facet Based IS solution 2022-12-07 22:59:21 +01:00
Luca Frosini fef4bd8479 Removed old commented code 2022-12-07 17:23:33 +01:00
Luca Frosini 018cb95905 Implementing FacetBased IS interaction 2022-12-07 17:21:14 +01:00
Luca Frosini c39484784f Refactoring configuration proxies and factories 2022-12-07 09:36:19 +01:00
Luca Frosini f1afe85744 Fixed test 2022-12-07 09:25:47 +01:00
Luca Frosini 0fd2f53a5d Reorganizing code 2022-12-06 18:29:31 +01:00
Luca Frosini f8c9033f3c Fixed getEncryptedSysAdminToken 2022-12-06 15:07:19 +01:00
Luca Frosini 527e418e16 Implemented configuration based on new IS 2022-12-06 14:59:51 +01:00
Luca Frosini b80a251896 Switching gcat to the new IS 2022-12-06 14:05:43 +01:00
Luca Frosini 5dabca9c30 Fixed indentation with tabs instead of spaces 2022-12-05 14:48:07 +01:00
Luca Frosini 32a6873e72 Renaming json query to have them ordered 2022-12-05 14:38:43 +01:00
Luca Frosini 814b88c1d1 Refactored classes 2022-12-05 14:31:10 +01:00
Luca Frosini 8638559d43 Added JSON query to be used to interact with new IS 2022-12-02 17:44:33 +01:00
Luca Frosini 9c9e57314a Added test 2022-12-02 17:01:15 +01:00
Luca Frosini bcc4496590 Added test 2022-12-02 17:01:06 +01:00
Luca Frosini d979198399 Implementing new IS interaction 2022-12-01 17:26:46 +01:00
Luca Frosini bcadc5b873 Removed unused import 2022-12-01 12:49:40 +01:00
Luca Frosini 335837e17c Reorganizing code to properly support new and old IS 2022-11-29 15:03:35 +01:00
Luca Frosini 08530b490f Reorganizing code to properly support new and old IS 2022-11-29 14:42:20 +01:00
Luca Frosini fdabed4a87 Moving configuration to the new IS 2022-11-29 12:33:42 +01:00
100 changed files with 10926 additions and 9221 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
target
.classpath
.project
/**/.DS_Store

View File

@ -3,7 +3,37 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for gCube Catalogue (gCat) Service
## [v2.4.1]
## [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]
@ -11,7 +41,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fixed item listing [#23901]
## [v2.4.0]
## [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]
@ -19,13 +49,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Explict request for approved items return only moderated and approved [#23696]
## [v2.3.0]
## [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]
## [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]
@ -38,7 +68,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- ClientID requests are now properly supported [#21903]
## [v2.1.0]
## [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]
@ -73,7 +103,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Switched JSON management to gcube-jackson [#19735]
## [v1.4.3] [r4.23.0] - 2020-06-19
## [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]
@ -104,7 +134,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Resource URL existence is not checked anymore [#16748]
## [v1.2.0]
## [v1.2.0-SNAPSHOT] - 2019-05-20
- 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]
@ -131,7 +161,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Refactored to use gcat-api library
## [v1.0.0]
## [v1.0.0-SNAPSHOT] - 2019-01-10
- First Release
- First Version

26
FUNDING.md Normal file
View File

@ -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);

View File

@ -13,7 +13,7 @@ This service allows any client to publish on the gCube Catalogue.
## Change log
See [Releases](https://code-repo.d4science.org/gCubeSystem/gcat/releases).
See [CHANGELOG.md](CHANGELOG.md).
## Authors
@ -25,15 +25,13 @@ Tell people how to cite this software.
* Cite an associated paper?
* Use a specific BibTeX entry for the software?
@Manual{,
title = {gCube Catalogue (gCat) Service},
author = {{Frosini, Luca}},
organization = {ISTI - CNR},
address = {Pisa, Italy},
year = 2019,
url = {http://www.gcube-system.org/}
}
@software{gcat,
author = {{Luca Frosini}},
title = {gCube Catalogue (gCat) Service},
abstract = {gCube Catalogue (gCat) Service allows the publication of items in the gCube Catalogue.},
url = {https://doi.org/10.5281/zenodo.7446641},
keywords = {Catalogue, D4Science, gCube}
}
## 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
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 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);
The projects leading to this software have received funding from a series of European Union programmes see [FUNDING.md](FUNDING.md)

View File

@ -17,12 +17,12 @@
# -- Project information -----------------------------------------------------
project = 'gCube Catalogue Service (aka gCat)'
project = 'gCube Catalogue (gCat) Service'
copyright = '2022, Luca Frosini (ISTI-CNR)'
author = 'Luca Frosini (ISTI-CNR)'
# The full version, including alpha/beta/rc tags
release = '2.4.1-SNAPSHOT'
release = '2.5.1'
# -- General configuration ---------------------------------------------------

484
docs/index.md Normal file
View File

@ -0,0 +1,484 @@
---
title: Welcome to gCube Catalogue Service (aka gCat) documentation
---
gCat is a RESTful application which 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 own REST
client application or using existing REST client plugins.
HTTP Statuses
-------------
Any successful operation returns *200 OK* HTTP status code. The create
operation returns *201 Created*. Any Background operation returns *202
Accepted*. Any operation which does not provide any content return *204
No Content*.
The most common error status 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 has
not 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 network error, 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
listing of collections and CRUD (Create Read Update Delete) operations
on instances.
:::{table} Supported operations
:align: center
:widths: grid
| Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent |
|-----------|-------------|-----|---------------------|------|------------|
| **Supported<br/>HTTP Methods** | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y |
| **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<br/>HTTP Methods** | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
| **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 |
| **Purge** | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N |
| **Purge** | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N |
### 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 than 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 service which
requires 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.
``` {.rest}
Accept: application/json
```
For any operation sending content to the service, it is necessary to
specify the **Content-Type** HTTP Header.
``` {.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](../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}
::: {.title}
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 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 operation 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 whcih summarise 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 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 summarize the allowed/forbidden operations depending
on: the role of the user and the state of the item.
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.
Java Client
===========
We provide the following Java Client out-of-the-box.
> ::: {.tip}
> ::: {.title}
> Tip
> :::
>
> If you\'re coding in Java, it is recommended that you use this Java
> Client.
> :::
**Maven Coordinates**
``` {.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}
> ::: {.title}
> Important
> :::
>
> The result of all methods is always a JSON object as per below:
> :::
``` {.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
``` {.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 gCore IS as gCore Endpoint with the
following parameter:
``` {.xml}
<ServiceClass>org.gcube.data-catalogue</ServiceClass>
<ServiceName>gcat</ServiceName>
```
The service can be discovered in the Facet Based IS as EService with the
following json query:
``` {.json}
{
"@class": "EService",
"consistsOf": [
{
"@class": "IsIdentifiedBy",
"target": {
"@class": "SoftwareFacet",
"group": "org.gcube.data-catalogue",
"name": "gcat"
}
}
]
}
```
Service Maven Coordinates
=========================
The maven coordinates of gCat service are:
``` {.xml}
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat</artifactId>
```

View File

@ -1,48 +1,11 @@
***********************************************************
Welcome to gCube Catalogue Service (aka gCat) documentation
***********************************************************
gCat is a RESTful application which exposes operations ...
gCat is a RESTful application that exposes operations via REST-API.
See the available REST-API on `its API docs <../api-docs/index.html>`_.
See the available `REST-API docs <../api-docs/index.html>`_.
The maven coordinates are:
.. code:: xml
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat</artifactId>
The service can be discovered in the gCore IS as gCore Endpoint with the following parameter:
.. code:: xml
<ServiceClass>org.gcube.data-catalogue</ServiceClass>
<ServiceName>gcat</ServiceName>
The service can be discovered in the Facet Based 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"
}
}
]
}
Base URL
========
@ -61,23 +24,23 @@ 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 own REST client application or using existing REST client plugins.
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 *200 OK* HTTP status code.
Any successful operation returns a *200 OK* HTTP status code.
The create operation returns *201 Created*.
Any Background operation returns *202 Accepted*.
Any operation which does not provide any content return *204 No Content*.
Any operation that does not provide any content returns *204 No Content*.
The most common error status a client can obtain are:
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 has not enough right to perform such request `<https://tools.ietf.org/html/rfc7235#section-3.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>`_;
@ -91,7 +54,7 @@ If you get a *500 Internal Server Error*, please report it in the `gCube ticketi
Please use this checklist before reporting an error:
* Replicate the request;
* The failure could be temporal due to network error, server issue and many other temporal issues. For this reason, please retry the request after a certain amount of time before reporting the issue;
* 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).
@ -140,12 +103,12 @@ gCat is a pure RESTful service. It uses standard HTTP Methods to perform a listi
.. [#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 really deleted or it was not found, i.e., **404 Not Found** error is suggested instead of **204 No Content**.
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 . OReilly. first ed. 2010
.. [#Allamaraju] Allamaraju S. RESTful Web Services Cookbook: Solutions for Improving Scalability and Simplicity. OReilly. first ed. 2010
About URL
@ -164,7 +127,7 @@ 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 than using it one time.
* 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>`_.
@ -174,7 +137,7 @@ 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 service which requires this action
* 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``.
@ -261,12 +224,12 @@ 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.
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 published by any allowed author (a Catalogue-Editor or above) but not available to the other users of the catalogue.
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**:
@ -286,7 +249,7 @@ To present the moderation operations, we use the following convention:
``initial_state`` can be ``none``, meaning the item does not exist.
The following are the allowed moderation operation on an item
The following are the allowed moderation operations on an item
``none`` ---**create** (*Author*)---> ``pending``
@ -299,14 +262,14 @@ The following are the allowed moderation operation on an item
``approved`` ---**update** (*Author*)---> ``pending``
Please check the table below whcih summarise the item collection opration and the allowed users/roles.
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 explicit action which does not change the status of the item:
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``
@ -316,7 +279,7 @@ Messages can be sent both with an action which changes the status of the item or
The following table summarize the allowed/forbidden operations depending on: the role of the user and the state of the item.
The following table summarizes the allowed/forbidden operations depending on the role of the user and the state of the item.
.. table::
@ -362,21 +325,25 @@ The following table summarises the addressee of the notification for any action.
.. table::
+------------+-------------------------+-----------------+
| Operation | Notified user/role |
+ +-------------------------+-----------------+
| | Catalogue-Moderators | Author |
+============+=========================+=================+
| Create | Yes | No |
+------------+-------------------------+-----------------+
| Update | Yes | Yes |
+------------+-------------------------+-----------------+
| Approve | Yes + Social Post if enabled for the VRE |
+------------+-------------------------+-----------------+
| Reject | Yes | Yes |
+------------+-------------------------+-----------------+
| Message | Yes | Yes |
+------------+-------------------------+-----------------+
+--------------+----------------------+--------+----------------------+
| 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 |
+--------------+----------------------+--------+----------------------+
@ -405,9 +372,81 @@ The service exposes `its methods <../api-docs/index.html>`_ using a standard nam
.. 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.*
@ -425,3 +464,46 @@ The service exposes `its methods <../api-docs/index.html>`_ using a standard nam
int count = item.count();
...
Service Discovery on IS
=======================
The service can be discovered in the gCore IS as gCore Endpoint with the following parameter:
.. code:: xml
<ServiceClass>org.gcube.data-catalogue</ServiceClass>
<ServiceName>gcat</ServiceName>
The service can be discovered in the Facet-Based 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>

View File

@ -8,16 +8,16 @@ To perform moderation tests in dev and preproduction infrastructure we use diffe
+---------------+---------------+----------------------------------------+
| User | Username | Role |
+===============+===============+========================================+
| Mister White | mister.white | Catalogue-Manager |
| 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 Blonde | mister.blonde | Catalogue-Admin + Catalogue-Moderator |
+---------------+---------------+----------------------------------------+
| Mister Brown | mister.brown | Catalogue-Moderator |
| Mister White | mister.white | Catalogue-Manager |
+---------------+---------------+----------------------------------------+

329
gcube/gcat-export.json Normal file
View File

@ -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>&nbsp;</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
}
]
}

40
pom.xml
View File

@ -5,14 +5,14 @@
<parent>
<artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId>
<version>1.1.0</version>
<version>1.2.0</version>
<relativePath />
</parent>
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat</artifactId>
<packaging>war</packaging>
<version>2.4.1</version>
<version>2.5.3</version>
<name>gCube Catalogue (gCat) Service</name>
<description>
This service allows any client to publish on the gCube Catalogue.
@ -35,7 +35,7 @@
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>gcube-smartgears-bom</artifactId>
<version>2.2.0</version>
<version>2.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -59,10 +59,15 @@
<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-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat-api</artifactId>
<version>[2.3.1,3.0.0-SNAPSHOT)</version>
<version>[2.3.2, 3.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.social-networking</groupId>
@ -72,7 +77,7 @@
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>authorization-utils</artifactId>
<version>[2.0.0, 3.0.0-SNAPSHOT)</version>
<version>[2.2.0, 3.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
@ -98,6 +103,10 @@
<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>
@ -152,6 +161,10 @@
<groupId>org.gcube.common</groupId>
<artifactId>gxHTTP</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>keycloak-client</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
@ -189,7 +202,7 @@
<dependency>
<groupId>org.gcube.data-publishing</groupId>
<artifactId>storagehub-application-persistence</artifactId>
<version>[3.0.0,4.0.0-SNAPSHOT)</version>
<version>[3.3.0-SNAPSHOT,4.0.0-SNAPSHOT)</version>
</dependency>
<!-- Used to automatically convert XML to JSON -->
@ -208,9 +221,9 @@
<!-- Used by Validator -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--
@ -333,6 +346,15 @@
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -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);
}
}

View File

@ -4,12 +4,14 @@ import javax.ws.rs.ApplicationPath;
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;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@ApplicationPath("/")
@ManagedBy(GCatInitializator.class)
public class ResourceInitializer extends ResourceConfig {
public ResourceInitializer() {

View File

@ -1,64 +0,0 @@
package org.gcube.gcat.configuration;
import java.util.HashMap;
import java.util.Map;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.gcat.persistence.ckan.CKANUserCache;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CatalogueConfigurationFactory {
private static final Map<String, ServiceCatalogueConfiguration> catalogueConfigurations;
static {
catalogueConfigurations = new HashMap<>();
}
protected static ServiceCatalogueConfiguration load(String context) {
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context);
return gCoreISConfigurationProxy.getCatalogueConfiguration();
}
public synchronized static ServiceCatalogueConfiguration getInstance() {
String context = SecretManagerProvider.instance.get().getContext();
ServiceCatalogueConfiguration catalogueConfiguration = catalogueConfigurations.get(context);
if(catalogueConfiguration == null) {
catalogueConfiguration = load(context);
catalogueConfigurations.put(context, catalogueConfiguration);
}
return catalogueConfiguration;
}
public synchronized static void renew() {
String context = SecretManagerProvider.instance.get().getContext();
catalogueConfigurations.remove(context);
ServiceCatalogueConfiguration catalogueConfiguration = load(context);
catalogueConfigurations.put(context, catalogueConfiguration);
}
public synchronized static void purge() {
// Remove the resource from IS
String context = SecretManagerProvider.instance.get().getContext();
catalogueConfigurations.remove(context);
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context);
gCoreISConfigurationProxy.delete();
}
public synchronized static ServiceCatalogueConfiguration createOrUpdate(ServiceCatalogueConfiguration catalogueConfiguration) throws Exception {
String context = SecretManagerProvider.instance.get().getContext();
catalogueConfigurations.remove(context);
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context, catalogueConfiguration);
catalogueConfiguration = gCoreISConfigurationProxy.createOrUpdateOnIS();
catalogueConfigurations.put(context, catalogueConfiguration);
// The supported organizations could be changed we need to empty the user cache for the context
// to avoid to miss to add an user in an organization which has been added.
CKANUserCache.emptyUserCache();
return catalogueConfiguration;
}
}

View File

@ -1,704 +0,0 @@
package org.gcube.gcat.configuration;
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.WebApplicationException;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
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.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.resources.gcore.GenericResource;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
import org.gcube.common.resources.gcore.ServiceEndpoint.Profile;
import org.gcube.common.resources.gcore.ServiceEndpoint.Property;
import org.gcube.common.resources.gcore.ServiceEndpoint.Runtime;
import org.gcube.common.resources.gcore.common.Platform;
import org.gcube.common.resources.gcore.utils.Group;
import org.gcube.gcat.api.GCatConstants;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.informationsystem.publisher.RegistryPublisher;
import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
import org.gcube.resources.discovery.icclient.ICFactory;
import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class GCoreISConfigurationProxy {
private static final Logger logger = LoggerFactory.getLogger(GCoreISConfigurationProxy.class);
// property to retrieve the master service endpoint into the /root scope
public final static String IS_ROOT_MASTER_PROPERTY_KEY = "IS_ROOT_MASTER"; // true, false.. missing means false as
public final static String DEFAULT_ORGANIZATION_PROPERTY_KEY = "DEFAULT_ORGANIZATION";
public final static String SUPPORTED_ORGANIZATION_PROPERTY_KEY = "SUPPORTED_ORGANIZATION";
public final static String API_KEY_PROPERTY_KEY = "API_KEY";
public final static String SOLR_INDEX_ADDRESS_PROPERTY_KEY = "SOLR_INDEX_ADDRESS";
public final static String SOCIAL_POST_PROPERTY_KEY = "SOCIAL_POST";
public final static String ALERT_USERS_ON_POST_CREATION_PROPERTY_KEY = "ALERT_USERS_ON_POST_CREATION";
public final static String MODERATION_ENABLED_KEY_PROPERTY_KEY = "MODERATION_ENABLED";
public static final Map<String, String> gCoreToConfigurationMapping;
public static final Map<String, String> configurationToGCoreMapping;
static {
gCoreToConfigurationMapping = new HashMap<>();
configurationToGCoreMapping = new HashMap<>();
gCoreToConfigurationMapping.put(API_KEY_PROPERTY_KEY, CatalogueConfiguration.SYS_ADMIN_TOKEN_KEY);
gCoreToConfigurationMapping.put(SOLR_INDEX_ADDRESS_PROPERTY_KEY, CatalogueConfiguration.SOLR_URL_KEY);
gCoreToConfigurationMapping.put(SOCIAL_POST_PROPERTY_KEY, CatalogueConfiguration.SOCIAL_POST_ENABLED_KEY);
gCoreToConfigurationMapping.put(ALERT_USERS_ON_POST_CREATION_PROPERTY_KEY, CatalogueConfiguration.NOTIFICATION_TO_USER_ENABLED_KEY);
gCoreToConfigurationMapping.put(MODERATION_ENABLED_KEY_PROPERTY_KEY, CatalogueConfiguration.MODERATION_ENABLED_KEY);
for(String key : gCoreToConfigurationMapping.keySet()) {
configurationToGCoreMapping.put(gCoreToConfigurationMapping.get(key), key);
}
}
// CKAN Instance info
private final static String OLD_CATEGORY = "Application";
private final static String OLD_NAME = "CKanDataCatalogue";
protected final String context;
protected ObjectMapper mapper;
protected ServiceCatalogueConfiguration catalogueConfiguration;
public GCoreISConfigurationProxy(String context) {
this.context = context;
this.mapper = new ObjectMapper();
}
public GCoreISConfigurationProxy(String context, ServiceCatalogueConfiguration catalogueConfiguration) {
this(context);
this.catalogueConfiguration = catalogueConfiguration;
this.mapper = new ObjectMapper();
}
public ServiceCatalogueConfiguration getCatalogueConfiguration() throws WebApplicationException {
if (catalogueConfiguration == null) {
catalogueConfiguration = getCatalogueConfigurationFromIS();
}
return catalogueConfiguration;
}
protected AccessPoint getAccessPoint(Profile profile) {
Group<AccessPoint> accessPoints = profile.accessPoints();
Iterator<AccessPoint> accessPointIterator = accessPoints.iterator();
AccessPoint accessPoint = accessPointIterator.next();
return accessPoint;
}
protected String getDefaultSolrURL(String ckanURL) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(ckanURL);
stringBuffer.append(ckanURL.endsWith("/")?"":"/");
stringBuffer.append("solr/");
return stringBuffer.toString();
}
protected ObjectNode setValue(ObjectNode node, String key, String value) throws IOException {
if(value.toLowerCase().compareTo("true")==0 || value.toLowerCase().compareTo("false")==0) {
node.put(key, Boolean.parseBoolean(value));
return node;
}
if(value.startsWith("{") || value.startsWith("[")){
JsonNode n = mapper.readTree(value);
node.set(key, n);
return node;
}
node.put(key, value);
return node;
}
protected ServiceCatalogueConfiguration getConfiguration(ServiceEndpoint serviceEndpoint) throws IOException {
Profile profile = serviceEndpoint.profile();
AccessPoint accessPoint = getAccessPoint(profile);
Map<String, Property> propertyMap = accessPoint.propertyMap();
ObjectNode node = mapper.createObjectNode();
node.put(CatalogueConfiguration.ID_KEY, serviceEndpoint.id());
for(String key : propertyMap.keySet()) {
String value = propertyMap.get(key).value().trim();
setValue(node, key, value);
}
return mapper.treeToValue(node, ServiceCatalogueConfiguration.class);
}
private List<ServiceEndpoint> getServiceEndpoints(String category, String name) {
SimpleQuery query = queryFor(ServiceEndpoint.class);
query.addCondition("$resource/Scopes/Scope/text() eq '" + SecretManagerProvider.instance.get().getContext() + "'");
query.addCondition("$resource/Profile/Category/text() eq '" + category + "'");
query.addCondition("$resource/Profile/Name/text() eq '" + name + "'");
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
List<ServiceEndpoint> serviceEndpoints = client.submit(query);
return serviceEndpoints;
}
protected ServiceEndpoint getServiceEndpoint() {
List<ServiceEndpoint> serviceEndpoints = getServiceEndpoints(GCatConstants.CONFIGURATION_CATEGORY, GCatConstants.CONFIGURATION_NAME);
if (serviceEndpoints==null || serviceEndpoints.size() == 0) {
logger.error("There is no {} having Category {} and Name {} in this context.",
ServiceEndpoint.class.getSimpleName(), GCatConstants.CONFIGURATION_CATEGORY, GCatConstants.CONFIGURATION_NAME);
return null;
}
ServiceEndpoint serviceEndpoint = serviceEndpoints.get(0);
return serviceEndpoint;
}
protected ServiceCatalogueConfiguration getCatalogueConfigurationFromIS() throws WebApplicationException {
ServiceEndpoint serviceEndpoint = getServiceEndpoint();
if(serviceEndpoint==null) {
return getOLDCatalogueConfigurationFromIS();
}
try {
return getConfiguration(serviceEndpoint);
}catch (Exception e) {
throw new InternalServerErrorException();
}
}
@Deprecated
private ServiceEndpoint getOldServiceEndpoint() {
List<ServiceEndpoint> serviceEndpoints = getServiceEndpoints(OLD_CATEGORY, OLD_NAME);
if (serviceEndpoints.size() == 0) {
logger.error("There is no {} having Category {} and Name {} in this context.",
ServiceEndpoint.class.getSimpleName(), OLD_CATEGORY, OLD_NAME);
return null;
}
ServiceEndpoint serviceEndpoint = null;
if (serviceEndpoints.size() > 1) {
logger.info("Too many {} having Category {} and Name {} in this context. Looking for the one that has the property {}",
ServiceEndpoint.class.getSimpleName(), OLD_CATEGORY, OLD_NAME, IS_ROOT_MASTER_PROPERTY_KEY);
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_ROOT_MASTER_PROPERTY_KEY);
String isMaster = entry != null ? entry.value() : null;
if (isMaster == null || !isMaster.equals("true")) {
continue;
}
// set this variable
serviceEndpoint = se;
return serviceEndpoint;
}
}
// if none of them was master, throw an exception
if (serviceEndpoint == null) {
throw new InternalServerErrorException(
"Too many catalogue configuration on IS and no one with MASTER property");
}
} else {
serviceEndpoint = serviceEndpoints.get(0);
}
return serviceEndpoint;
}
@Deprecated
protected ServiceCatalogueConfiguration getOLDCatalogueConfigurationFromIS() {
ServiceCatalogueConfiguration catalogueConfiguration = new ServiceCatalogueConfiguration(context);
try {
// boolean mustBeUpdated = false;
ServiceEndpoint serviceEndpoint = getOldServiceEndpoint();
if (serviceEndpoint == null) {
throw new NotFoundException("No configuration found in this context");
}
// catalogueConfiguration.setID(serviceEndpoint.id());
Profile profile = serviceEndpoint.profile();
AccessPoint accessPoint = getAccessPoint(profile);
// add this host
String ckanURL = accessPoint.address();
catalogueConfiguration.setCkanURL(ckanURL);
Map<String, Property> propertyMap = accessPoint.propertyMap();
// retrieve sys admin token
String encryptedSysAdminToken = propertyMap.get(API_KEY_PROPERTY_KEY).value();
catalogueConfiguration.setEncryptedSysAdminToken(encryptedSysAdminToken);
String defaultOrganization = CatalogueConfiguration.getOrganizationName(context);
String solrURL = null;
if (propertyMap.containsKey(SOLR_INDEX_ADDRESS_PROPERTY_KEY)) {
solrURL = propertyMap.get(SOLR_INDEX_ADDRESS_PROPERTY_KEY).value();
}else {
solrURL = getDefaultSolrURL(ckanURL);
}
catalogueConfiguration.setSolrURL(solrURL);
// retrieve option to check if the social post has to be made
Boolean socialPostEnabled = true;
if (propertyMap.containsKey(SOCIAL_POST_PROPERTY_KEY)) {
if (propertyMap.get(SOCIAL_POST_PROPERTY_KEY).value().trim().equalsIgnoreCase("false")) {
socialPostEnabled = false;
}
}
catalogueConfiguration.setSocialPostEnabled(socialPostEnabled);
// retrieve option for user alert
boolean notificationToUsersEnabled = false; // default is false
if (propertyMap.containsKey(ALERT_USERS_ON_POST_CREATION_PROPERTY_KEY)) {
if (propertyMap.get(ALERT_USERS_ON_POST_CREATION_PROPERTY_KEY).value().trim()
.equalsIgnoreCase("true")) {
notificationToUsersEnabled = true;
}
}
catalogueConfiguration.setNotificationToUsersEnabled(notificationToUsersEnabled);
boolean moderationEnabled = false; // default is false
if (propertyMap.containsKey(MODERATION_ENABLED_KEY_PROPERTY_KEY)) {
if (propertyMap.get(MODERATION_ENABLED_KEY_PROPERTY_KEY).value().trim().equalsIgnoreCase("true")) {
moderationEnabled = true;
}
}
catalogueConfiguration.setModerationEnabled(moderationEnabled);
Set<String> supportedOrganizations = getSupportedOrganizationsFromGenericResource();
if (supportedOrganizations != null) {
catalogueConfiguration.setSupportedOrganizations(supportedOrganizations);
if(defaultOrganization==null) {
defaultOrganization = supportedOrganizations.toArray(new String[supportedOrganizations.size()])[0];
catalogueConfiguration.setDefaultOrganization(defaultOrganization);
}
}
ServiceCKANDB ckanDB = getCKANDBFromIS();
catalogueConfiguration.setCkanDB(ckanDB);
} catch (WebApplicationException e) {
throw e;
} catch (Exception e) {
throw new InternalServerErrorException("Error while getting configuration on IS", e);
}
return catalogueConfiguration;
}
// CKAN Instance info
@Deprecated
private final static String CKAN_DB_SERVICE_ENDPOINT_CATEGORY= "Database";
@Deprecated
private final static String CKAN_DB_SERVICE_ENDPOINT_NAME = "CKanDatabase";
@Deprecated
protected ServiceCKANDB getCKANDBFromIS() {
try {
List<ServiceEndpoint> serviceEndpoints = getServiceEndpoints(CKAN_DB_SERVICE_ENDPOINT_CATEGORY, CKAN_DB_SERVICE_ENDPOINT_NAME);
if(serviceEndpoints.size() == 0) {
String error = String.format("There is no %s having category '%s' and name '%s' in this context.",
ServiceEndpoint.class.getSimpleName(), CKAN_DB_SERVICE_ENDPOINT_CATEGORY, CKAN_DB_SERVICE_ENDPOINT_NAME);
logger.error(error);
throw new InternalServerErrorException(error);
}
ServiceEndpoint serviceEndpoint = null;
if(serviceEndpoints.size() > 1) {
logger.info("Too many {} having category {} and name {} in this context. Looking for the one that has the property {}",
ServiceEndpoint.class.getSimpleName(), CKAN_DB_SERVICE_ENDPOINT_CATEGORY,
CKAN_DB_SERVICE_ENDPOINT_NAME);
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_ROOT_MASTER_PROPERTY_KEY);
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();
String host = accessPoint.address();
String db = accessPoint.name();
ServiceCKANDB ckanDB = new ServiceCKANDB();
String url = String.format("jdbc:postgresql://%s/%s", host, db);
ckanDB.setUrl(url);
ckanDB.setUsername(accessPoint.username());
ckanDB.setEncryptedPassword(accessPoint.password());
return ckanDB;
}
return null;
} catch(WebApplicationException e) {
throw e;
} catch(Exception e) {
throw new InternalServerErrorException("Error while getting configuration on IS", e);
}
}
@Deprecated
public static final String GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS = "ApplicationProfile";
@Deprecated
public static final String GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS = "Supported CKAN Organizations";
@Deprecated
public static final String GENERIC_RESOURCE_CKAN_ORGANIZATIONS = "CKANOrganizations";
@Deprecated
private List<GenericResource> getGenericResources() {
SimpleQuery query = ICFactory.queryFor(GenericResource.class);
query.addCondition(String.format("$resource/Profile/SecondaryType/text() eq '%s'",
GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS));
query.addCondition(
String.format("$resource/Profile/Name/text() eq '%s'", GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS));
DiscoveryClient<GenericResource> client = ICFactory.clientFor(GenericResource.class);
List<GenericResource> genericResources = client.submit(query);
return genericResources;
}
protected String marshallSupportedOrganizations() throws JsonProcessingException {
Set<String> supportedOrganizations = catalogueConfiguration.getSupportedOrganizations();
return marshallSupportedOrganizations(supportedOrganizations);
}
protected String marshallSupportedOrganizations(Set<String> supportedOrganizations) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode arrayNode = objectMapper.createArrayNode();
for(String org : supportedOrganizations) {
arrayNode.add(org);
}
return objectMapper.writeValueAsString(arrayNode);
}
@Deprecated
protected Set<String> unmarshallSupportedOrganizations(String supportedOrganizationsJsonArray){
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(supportedOrganizationsJsonArray);
ArrayNode array = (ArrayNode) jsonNode.get(GENERIC_RESOURCE_CKAN_ORGANIZATIONS);
Set<String> supportedOrganizations = new HashSet<>(array.size());
for (int i = 0; i < array.size(); i++) {
String o = array.get(i).asText();
supportedOrganizations.add(o);
}
logger.debug("Supported CKAN Organization for current Context ({}) are {}", context,
supportedOrganizations);
return supportedOrganizations;
} catch (Exception e) {
return null;
}
}
@Deprecated
protected Set<String> getSupportedOrganizationsFromGenericResource() {
List<GenericResource> genericResources = getGenericResources();
if (genericResources == null || genericResources.size() == 0) {
logger.trace(
"{} with SecondaryType {} and Name %s not found. Item will be only be created in {} CKAN organization",
GenericResource.class.getSimpleName(), GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS,
GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS, ServiceCatalogueConfiguration.getOrganizationName(context));
return null;
}
GenericResource genericResource = genericResources.get(0);
String supportedOrganizationsJsonArray = genericResource.profile().body().getTextContent();
Set<String> supportedOrganizatins = unmarshallSupportedOrganizations(supportedOrganizationsJsonArray);
return supportedOrganizatins;
}
@Deprecated
public void deleteOldConfiguration() {
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
deleteOldConfiguration(registryPublisher);
}
@Deprecated
protected void deleteOldConfiguration(RegistryPublisher registryPublisher) {
ServiceEndpoint serviceEndpoint = getOldServiceEndpoint();
if(serviceEndpoint!=null) {
registryPublisher.remove(serviceEndpoint);
}
List<GenericResource> genericResources = getGenericResources();
if(genericResources!=null) {
for(GenericResource genericResource : genericResources) {
registryPublisher.remove(genericResource);
}
}
}
public void delete() {
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
ServiceEndpoint serviceEndpoint = getServiceEndpoint();
if(serviceEndpoint!=null) {
registryPublisher.remove(serviceEndpoint);
}
}
protected Property addProperty(Group<Property> properties, String name, String value) {
return addProperty(properties, name, value, false);
}
protected Property addProperty(Group<Property> properties, String name, String value, boolean encrypted) {
Property property = new Property();
property.nameAndValue(name, value);
property.encrypted(encrypted);
properties.add(property);
return property;
}
protected Group<Property> setAccessPointProperties(AccessPoint accessPoint, String address, boolean update) throws JsonProcessingException {
accessPoint.description(String.format("Access Point %s by gcat %s", update ? "updated" : "created", getGcatVersion().toString()));
accessPoint.address(address);
accessPoint.name(GCatConstants.CONFIGURATION_NAME);
Group<Property> properties = accessPoint.properties();
JsonNode jsonNode = mapper.valueToTree(catalogueConfiguration);
Iterator<String> iterator = jsonNode.fieldNames();
while (iterator.hasNext()) {
String key = iterator.next();
if(key.compareTo(CatalogueConfiguration.ID_KEY)==0) {
continue;
}
if(key.compareTo(CatalogueConfiguration.SYS_ADMIN_TOKEN_KEY)==0) {
addProperty(properties, key, catalogueConfiguration.getEncryptedSysAdminToken(), true);
continue;
}
JsonNode valueJsonNode = jsonNode.get(key);
String value = valueJsonNode.toString();
if(valueJsonNode.isTextual()) {
value = valueJsonNode.asText();
}
addProperty(properties, key, value);
}
return properties;
}
protected Version getGcatVersion() {
try {
ApplicationContext applicationContext = ContextProvider.get();
ApplicationConfiguration applicationConfiguration = applicationContext.configuration();
Version version = new Version(applicationConfiguration.version());
return version;
}catch (Exception e) {
return new Version("2.2.0");
}
}
/**
* Set the version of gcat so that in future implementation
* we can understand if the configuration must be updated.
* @param platform
* @return the platform
*/
protected Platform setVersion(Platform platform) {
Version version = getGcatVersion();
platform.version((short) version.getMajor());
platform.minorVersion((short) version.getMinor());
platform.revisionVersion((short) version.getRevision());
platform.buildVersion((short) 0);
return platform;
}
protected Platform setPlatformProperty(Platform platform) {
/*
* <Platform>
* <Name>gcat</Name>
* <!-- The version of gcat -->
* <Version>2</Version>
* <MinorVersion>2</MinorVersion>
* <RevisionVersion>0</RevisionVersion>
* <BuildVersion>0</BuildVersion>
* </Platform>
*/
platform.name(GCatConstants.SERVICE_NAME);
platform = setVersion(platform);
return platform;
}
private String getRunningOn(ContainerConfiguration containerConfiguration) {
return String.format("%s:%s", containerConfiguration.hostname(), containerConfiguration.port());
}
protected Runtime setRuntimeProperties(Runtime runtime) {
try {
ApplicationContext applicationContext = ContextProvider.get();
ContainerContext containerContext = applicationContext.container();
ContainerConfiguration containerConfiguration = containerContext.configuration();
String runningOn = getRunningOn(containerConfiguration);
runtime.hostedOn(runningOn);
runtime.ghnId(containerContext.id());
runtime.status(applicationContext.configuration().mode().toString());
}catch (Exception e) {
runtime.hostedOn("localhost");
runtime.ghnId("");
runtime.status("READY");
}
return runtime;
}
protected Profile setProfileProperties(Profile profile, boolean update) {
/*
* <Profile>
* <Category>Application</Category>
* <Name>CKanDataCatalogue</Name>
* <Description>gCat Configuration created/updated by the service via REST</Description>
*/
profile.category(GCatConstants.CONFIGURATION_CATEGORY);
profile.name(GCatConstants.CONFIGURATION_NAME);
profile.description(String.format("gCat configuration %s by the service via REST", update ? "updated" : "created"));
return profile;
}
// @Deprecated
// protected boolean isRootMaster(ServiceEndpoint serviceEndpoint) {
// Profile profile = serviceEndpoint.profile();
// AccessPoint accessPoint = getAccessPoint(profile);
// Map<String, Property> propertyMap = accessPoint.propertyMap();
// if (propertyMap.containsKey(IS_ROOT_MASTER_PROPERTY_KEY)) {
// if (propertyMap.get(IS_ROOT_MASTER_PROPERTY_KEY).value().trim().equalsIgnoreCase("true")) {
// return true;
// }
// }
// return false;
// }
protected ServiceEndpoint createServiceEndpoint(ServiceEndpoint serviceEndpoint) throws Exception {
boolean update = true;
if(serviceEndpoint==null) {
serviceEndpoint = new ServiceEndpoint();
serviceEndpoint.setId(catalogueConfiguration.getID());
update = false;
}
Profile profile = serviceEndpoint.newProfile();
profile = setProfileProperties(profile, update);
Platform platform = profile.newPlatform();
setPlatformProperty(platform);
Runtime runtime = profile.newRuntime();
runtime = setRuntimeProperties(runtime);
Group<AccessPoint> accessPoints = profile.accessPoints();
AccessPoint accessPoint = accessPoints.add();
setAccessPointProperties(accessPoint, runtime.hostedOn(), update);
return serviceEndpoint;
}
public ServiceCatalogueConfiguration createOnIS() throws Exception {
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
String id = catalogueConfiguration.getID();
if(id==null || id.compareTo("")==0) {
id = UUID.randomUUID().toString();
catalogueConfiguration.setID(id);
}
ServiceEndpoint serviceEndpoint = createServiceEndpoint(null);
registryPublisher.create(serviceEndpoint);
return catalogueConfiguration;
}
public ServiceCatalogueConfiguration updateOnIS(ServiceEndpoint serviceEndpoint) throws Exception {
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
String id = serviceEndpoint.id();
catalogueConfiguration.setID(id);
serviceEndpoint = createServiceEndpoint(serviceEndpoint);
registryPublisher.update(serviceEndpoint);
return catalogueConfiguration;
}
public ServiceCatalogueConfiguration createOrUpdateOnIS() throws Exception {
ServiceEndpoint serviceEndpoint = getServiceEndpoint();
if(serviceEndpoint!=null) {
// It's an update
updateOnIS(serviceEndpoint);
}else {
// It's a create
createOnIS();
}
return catalogueConfiguration;
}
}

View File

@ -1,62 +0,0 @@
package org.gcube.gcat.configuration;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import org.gcube.com.fasterxml.jackson.annotation.JsonGetter;
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
import org.gcube.com.fasterxml.jackson.annotation.JsonSetter;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.gcat.api.configuration.CKANDB;
/**
* @author Luca Frosini (ISTI-CNR)
*/
public class ServiceCKANDB extends CKANDB {
public static final String PASSWORD_KEY = "password";
protected String encryptedPassword;
@JsonIgnore
public String getPassword() {
return password;
}
@JsonIgnore
public String getPlainPassword() {
return password;
}
@JsonGetter(value=PASSWORD_KEY)
public String getEncryptedPassword() {
return encryptedPassword;
}
public void setEncryptedPassword(String encryptedPassword) throws Exception {
this.encryptedPassword = encryptedPassword;
this.password = StringEncrypter.getEncrypter().decrypt(encryptedPassword);
}
public void setPlainPassword(String plainPassword) throws Exception {
this.password = plainPassword;
this.encryptedPassword = StringEncrypter.getEncrypter().encrypt(plainPassword);
}
@Override
@JsonSetter(value = PASSWORD_KEY)
public void setPassword(String password) {
try {
try {
this.password = StringEncrypter.getEncrypter().decrypt(password);
this.encryptedPassword = password;
}catch (IllegalBlockSizeException | BadPaddingException e) {
this.password = password;
this.encryptedPassword = StringEncrypter.getEncrypter().encrypt(password);
}
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,142 +0,0 @@
package org.gcube.gcat.configuration;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import org.gcube.com.fasterxml.jackson.annotation.JsonGetter;
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
import org.gcube.com.fasterxml.jackson.annotation.JsonSetter;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.gcat.api.configuration.CKANDB;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.api.roles.Role;
import org.gcube.gcat.persistence.ckan.CKANUser;
import org.gcube.gcat.persistence.ckan.CKANUserCache;
/**
* @author Luca Frosini (ISTI-CNR)
*/
public class ServiceCatalogueConfiguration extends CatalogueConfiguration {
protected ObjectMapper mapper;
public ServiceCatalogueConfiguration() {
super();
mapper = new ObjectMapper();
}
public ServiceCatalogueConfiguration(String context) {
super(context);
mapper = new ObjectMapper();
}
@JsonIgnore
protected String encryptedSysAdminToken;
@JsonIgnore
public String getSysAdminToken() {
return sysAdminToken;
}
@JsonIgnore
public String getPlainSysAdminToken() {
return sysAdminToken;
}
@JsonGetter(value=SYS_ADMIN_TOKEN_KEY)
public String getEncryptedSysAdminToken() {
return encryptedSysAdminToken;
}
public void setEncryptedSysAdminToken(String encryptedSysAdminToken) throws Exception {
this.encryptedSysAdminToken = encryptedSysAdminToken;
this.sysAdminToken = StringEncrypter.getEncrypter().decrypt(encryptedSysAdminToken);
}
public void setPlainSysAdminToken(String plainSysAdminToken) throws Exception {
this.sysAdminToken = plainSysAdminToken;
this.encryptedSysAdminToken = StringEncrypter.getEncrypter().encrypt(plainSysAdminToken);
}
@Override
@JsonSetter(value = SYS_ADMIN_TOKEN_KEY)
public void setSysAdminToken(String sysAdminToken) {
try {
try {
this.sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken);
this.encryptedSysAdminToken = sysAdminToken;
}catch (IllegalBlockSizeException | BadPaddingException e) {
this.sysAdminToken = sysAdminToken;
this.encryptedSysAdminToken = StringEncrypter.getEncrypter().encrypt(sysAdminToken);
}
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
@JsonGetter(value = CKAN_DB_KEY)
public ServiceCKANDB getCkanDB() {
return (ServiceCKANDB) ckanDB;
}
@Override
public void setCkanDB(CKANDB ckanDB) {
this.ckanDB = new ServiceCKANDB();
this.ckanDB.setUrl(ckanDB.getUrl());
this.ckanDB.setUsername(ckanDB.getUsername());
this.ckanDB.setPassword(ckanDB.getPassword());
}
@JsonSetter(value=CKAN_DB_KEY)
public void setCkanDB(ServiceCKANDB ckanDB) {
this.ckanDB = ckanDB;
}
public ObjectNode toObjetcNode() throws JsonProcessingException {
return toObjetcNode(false);
}
public ObjectNode toObjetcNode(boolean decryptedValues) throws JsonProcessingException {
ObjectNode configuration = mapper.valueToTree(this);
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
if(ckanUser.getRole().ordinal() < Role.MANAGER.ordinal()) {
configuration.remove(ServiceCatalogueConfiguration.SYS_ADMIN_TOKEN_KEY);
configuration.remove(ServiceCatalogueConfiguration.CKAN_DB_KEY);
}else {
if(decryptedValues) {
configuration.put(ServiceCatalogueConfiguration.SYS_ADMIN_TOKEN_KEY, getPlainSysAdminToken());
ObjectNode node = (ObjectNode) configuration.get(ServiceCatalogueConfiguration.CKAN_DB_KEY);
node.put(ServiceCKANDB.PASSWORD_KEY, ((ServiceCKANDB) ckanDB).getPlainPassword());
}
}
return configuration;
}
public String toJsonString() throws Exception {
return toJsonString(false);
}
public String toJsonString(boolean decryptedValues) throws Exception {
ObjectNode objectNode = toObjetcNode(decryptedValues);
return mapper.writeValueAsString(objectNode);
}
public static ServiceCatalogueConfiguration getServiceCatalogueConfiguration(String json) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ServiceCatalogueConfiguration catalogueConfiguration = mapper.readValue(json, ServiceCatalogueConfiguration.class);
return catalogueConfiguration;
}
public static ServiceCatalogueConfiguration getServiceCatalogueConfiguration(ObjectNode objectNode) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ServiceCatalogueConfiguration catalogueConfiguration = mapper.treeToValue(objectNode, ServiceCatalogueConfiguration.class);
return catalogueConfiguration;
}
}

View File

@ -1,127 +0,0 @@
package org.gcube.gcat.configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class Version implements Comparable<Version> {
/**
* Regex validating the version
*/
public final static String VERSION_REGEX = "^[1-9][0-9]{0,}\\.(0|([1-9][0-9]{0,}))\\.(0|([1-9][0-9]{0,}))";
private final static Pattern VERSION_PATTERN;
static {
VERSION_PATTERN = Pattern.compile(VERSION_REGEX);
}
protected int major;
protected int minor;
protected int revision;
protected Version(){}
public Version(String version) {
setVersion(version);
}
public Version(int major, int minor, int revision) {
this.major = major;
this.minor = minor;
this.revision = revision;
}
public void setVersion(String version) {
Matcher matcher = VERSION_PATTERN.matcher(version);
if(!matcher.find()) {
throw new RuntimeException("The provided version (i.e. " + version + ") MUST comply with the regex " + VERSION_REGEX);
}
String matched = matcher.group(0);
String[] parts = matched.split("\\.");
this.major = Integer.valueOf(parts[0]);
this.minor = Integer.valueOf(parts[1]);
this.revision = Integer.valueOf(parts[2]);
}
public int getMajor() {
return major;
}
protected void setMajor(int major) {
this.major = major;
}
public int getMinor() {
return minor;
}
protected void setMinor(int minor) {
this.minor = minor;
}
public int getRevision() {
return revision;
}
protected void setRevision(int revision) {
this.revision = revision;
}
@Override
public String toString() {
return major + "." + minor + "." + revision;
}
@Override
public int compareTo(Version other) {
if(other == null) {
return 1;
}
int compare = Integer.compare(major, other.major);
if(compare!=0) {
return compare;
}
compare = Integer.compare(minor, other.minor);
if(compare!=0) {
return compare;
}
compare = Integer.compare(revision, other.revision);
return compare;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + major;
result = prime * result + minor;
result = prime * result + revision;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Version other = (Version) obj;
if (major != other.major)
return false;
if (minor != other.minor)
return false;
if (revision != other.revision)
return false;
return true;
}
}

View File

@ -1,34 +0,0 @@
package org.gcube.gcat.moderation.thread;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.gcat.api.moderation.CMItemStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class FakeModerationThread extends ModerationThread {
private static final Logger logger = LoggerFactory.getLogger(FakeModerationThread.class);
@Override
protected void postMessage(String message) throws Exception {
logger.info("gCat is sending a message to the {} for item '{}' (id={}). ItemStatus={}, Message=\"{}\"",
ModerationThread.class.getSimpleName(), itemName, itemID, cmItemStatus, message);
}
@Override
public void postUserMessage(CMItemStatus cmItemStatus, String userMessage) throws Exception {
logger.info("{} is sending a message to the {} for item '{}' (id={}). ItemStatus={}, Message=\"{}\"",
SecretManagerProvider.instance.get().getUser().getUsername(),
ModerationThread.class.getSimpleName(), itemName, itemID, cmItemStatus, userMessage);
}
@Override
protected void createModerationThread() throws Exception {
logger.info("Creating {} for item '{}' (id={})", ModerationThread.class.getSimpleName(), itemName, itemID);
}
}

View File

@ -1,166 +0,0 @@
package org.gcube.gcat.moderation.thread;
import java.util.HashMap;
import java.util.Map;
//import java.util.HashMap;
//import java.util.Map;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
//import org.gcube.common.authorization.utils.manager.SecretManager;
//import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
//import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.api.moderation.CMItemStatus;
import org.gcube.gcat.moderation.thread.social.notifications.SocialNotificationModerationThread;
import org.gcube.gcat.persistence.ckan.CKANUser;
//import org.gcube.portlets.user.uriresolvermanager.UriResolverManager;
//import org.gcube.portlets.user.uriresolvermanager.resolvers.query.CatalogueResolverQueryString.MODERATION_OP;
//import org.gcube.portlets.user.uriresolvermanager.resolvers.query.CatalogueResolverQueryStringBuilder;
import org.gcube.portlets.user.uriresolvermanager.UriResolverManager;
import org.gcube.portlets.user.uriresolvermanager.resolvers.query.CatalogueResolverQueryString.MODERATION_OP;
import org.gcube.portlets.user.uriresolvermanager.resolvers.query.CatalogueResolverQueryStringBuilder;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public abstract class ModerationThread {
protected String itemID;
protected String itemName;
protected String itemTitle;
protected String itemURL;
protected String itemAuthorCkanUsername;
protected String moderationURL;
protected boolean create;
protected CMItemStatus cmItemStatus;
protected boolean itemAuthor;
protected CKANUser ckanUser;
protected ObjectMapper objectMapper;
public static ModerationThread getDefaultInstance() {
// return new FakeModerationThread();
// return new SocialMessageModerationThread();
return new SocialNotificationModerationThread();
}
public ModerationThread() {
this.objectMapper = new ObjectMapper();
this.itemAuthor = false;
this.create = false;
this.cmItemStatus = CMItemStatus.PENDING;
}
public void setItemCoordinates(String itemID, String itemName, String itemTitle, String itemURL) {
this.itemID = itemID;
this.itemName = itemName;
this.itemTitle = itemTitle;
this.itemURL = itemURL;
}
public void setItemAuthor(boolean itemAuthor) {
this.itemAuthor = itemAuthor;
}
public String getItemAuthorCkanUsername() {
return itemAuthorCkanUsername;
}
public void setItemAuthorCkanUsername(String itemAuthorCkanUsername) {
this.itemAuthorCkanUsername = itemAuthorCkanUsername;
}
public void setCKANUser(CKANUser ckanUser) {
this.ckanUser = ckanUser;
}
public String getModerationURL() {
if(moderationURL==null) {
try {
SecretManager secretManager = SecretManagerProvider.instance.get();
String context = secretManager.getContext();
UriResolverManager resolver = new UriResolverManager("CTLG");
Map<String, String> params = new HashMap<String, String>();
params.put("gcube_scope", context); //e.g. /gcube/devsec/devVRE
params.put("entity_context", "organization");
params.put("entity_name", CatalogueConfiguration.getOrganizationName(context)); //e.g. devvre
CatalogueResolverQueryStringBuilder builder = new CatalogueResolverQueryStringBuilder(itemName); //item name under moderation
builder.itemStatus(cmItemStatus.name()). //e.g. pending, approved, rejected
moderation(MODERATION_OP.show);
String queryString = builder.buildQueryParametersToQueryString();
params.put(CatalogueResolverQueryStringBuilder.QUERY_STRING_PARAMETER, queryString);
moderationURL = resolver.getLink(params, true);
}catch (Exception e) {
return itemURL;
}
}
return moderationURL;
}
/**
* The message is sent as gCat
* @param message
* @throws Exception
*/
protected abstract void postMessage(String message) throws Exception;
/**
* The message is sent as User
*
* @param cmItemStatus
* @param userMessage
* @throws Exception
*/
public abstract void postUserMessage(CMItemStatus cmItemStatus, String userMessage) throws Exception;
protected abstract void createModerationThread() throws Exception;
public void postItemCreated() throws Exception {
createModerationThread();
create = true;
cmItemStatus = CMItemStatus.PENDING;
String fullName = ckanUser.getNameSurname();
String message = String.format(
"@**%s** created the item with name '%s' (id='%s'). The item is now in **%s** state and must be moderated.",
fullName, itemName, itemID, cmItemStatus.getFancyValue());
postMessage(message);
}
public void postItemUpdated() throws Exception {
cmItemStatus = CMItemStatus.PENDING;
String fullName = ckanUser.getNameSurname();
String message = String.format(
"@**%s** updated the item with name '%s' (id='%s'). The item is now in **%s** state and must be moderated.",
fullName, itemName, itemID, cmItemStatus.getFancyValue());
postMessage(message);
}
public void postItemRejected(String userMessage) throws Exception {
cmItemStatus = CMItemStatus.REJECTED;
String fullName = ckanUser.getNameSurname();
String message = String.format(
"@**%s** **%s** the item with name '%s' (id='%s'). The author can delete the item or update it to try to meet moderators requests if any.",
fullName, cmItemStatus.getFancyValue(), itemName, itemID);
postMessage(message);
postUserMessage(cmItemStatus, userMessage);
}
public void postItemApproved(String userMessage) throws Exception {
cmItemStatus = CMItemStatus.APPROVED;
String fullName = ckanUser.getNameSurname();
String message = String.format(
"@**%s** **%s** the item with name '%s' (id='%s'). The item is now available in the catalogue. The item is available at %s",
fullName, cmItemStatus.getFancyValue(), itemName, itemID, itemURL);
postMessage(message);
postUserMessage(cmItemStatus, userMessage);
}
}

View File

@ -1,298 +0,0 @@
package org.gcube.gcat.moderation.thread.social.notifications;
import java.net.URL;
import java.util.Set;
import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.gcat.api.moderation.CMItemStatus;
import org.gcube.gcat.api.moderation.Moderated;
import org.gcube.gcat.moderation.thread.ModerationThread;
import org.gcube.gcat.persistence.ckan.CKANUser;
import org.gcube.gcat.social.SocialUsers;
import org.gcube.gcat.utils.Constants;
import org.gcube.social_networking.social_networking_client_library.NotificationClient;
import org.gcube.social_networking.socialnetworking.model.beans.catalogue.CatalogueEvent;
import org.gcube.social_networking.socialnetworking.model.beans.catalogue.CatalogueEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class SocialNotificationModerationThread extends ModerationThread {
private static final Logger logger = LoggerFactory.getLogger(SocialNotificationModerationThread.class);
public static final String AUTHOR = "Author";
protected CatalogueEventType catalogueEventType;
protected boolean comment;
protected static final boolean notificationSentByGCat;
static {
notificationSentByGCat = false;
}
public SocialNotificationModerationThread() {
super();
this.comment = false;
}
/**
* Create the message for an item that is created/updated
*/
protected void notifyItemToBeManaged() throws Exception {
/*
* An example of created message is:
*
* [mister x] created/updated the item "[TITLE]". You are kindly requested to review it and decide either to APPROVE or REJECT it. [Go to catalogue]
*
*/
String fullName = ckanUser.getNameSurname();
StringBuffer stringBuffer = new StringBuffer();
if(notificationSentByGCat) {
stringBuffer.append(fullName);
}
stringBuffer.append(create ? " created " : " updated ");
stringBuffer.append("the item ");
stringBuffer = addQuotedTitle(stringBuffer);
stringBuffer.append(". You are kindly requested to review it and decide either to APPROVE or REJECT it. ");
postMessage(stringBuffer.toString());
}
public void postItemCreated() throws Exception {
create = true;
cmItemStatus = CMItemStatus.PENDING;
catalogueEventType = CatalogueEventType.ITEM_SUBMITTED;
notifyItemToBeManaged();
}
public void postItemUpdated() throws Exception {
create = false;
cmItemStatus = CMItemStatus.PENDING;
catalogueEventType = CatalogueEventType.ITEM_UPDATED;
notifyItemToBeManaged();
}
protected StringBuffer addUserWithRole(String fullName, String role, StringBuffer stringBuffer, boolean addUserFullName) {
if(addUserFullName) {
stringBuffer.append(fullName);
}
if(role!=null) {
stringBuffer.append(" [");
stringBuffer.append(role);
stringBuffer.append("] ");
}
return stringBuffer;
}
protected StringBuffer addUserWithRole(String fullName, String role, StringBuffer stringBuffer) {
return addUserWithRole(fullName, role, stringBuffer, notificationSentByGCat);
}
public void postItemManaged(String userMessage) throws Exception {
/*
* [mister x] rejected the item "[TITLE]" with this accompanying message "[MESSAGE]". To resubmit it [Go to catalogue]
*
* [mister x] approved the item "[TITLE]" with this accompanying message "[MESSAGE]". [Go to catalogue]
*/
create = false;
String fullName = ckanUser.getNameSurname();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer = addUserWithRole(fullName, Moderated.CATALOGUE_MODERATOR, stringBuffer);
stringBuffer.append(cmItemStatus.getValue());
stringBuffer.append(" the item ");
stringBuffer = addQuotedTitle(stringBuffer);
if(userMessage!=null && userMessage.length()>0) {
stringBuffer.append(" with this accompanying message \"");
stringBuffer.append(userMessage);
stringBuffer.append("\"");
}
stringBuffer.append(".");
if(cmItemStatus == CMItemStatus.REJECTED) {
stringBuffer.append(" To resubmit it ");
}
postMessage(stringBuffer.toString());
}
@Override
public void postItemRejected(String userMessage) throws Exception {
create = false;
cmItemStatus = CMItemStatus.REJECTED;
catalogueEventType = CatalogueEventType.ITEM_REJECTED;
postItemManaged(userMessage);
}
@Override
public void postItemApproved(String userMessage) throws Exception {
create = false;
cmItemStatus = CMItemStatus.APPROVED;
catalogueEventType = CatalogueEventType.ITEM_PUBLISHED;
postItemManaged(userMessage);
}
protected StringBuffer addQuotedTitle(StringBuffer stringBuffer, String quotingCharacter) {
stringBuffer.append(quotingCharacter);
stringBuffer.append(itemTitle);
stringBuffer.append(quotingCharacter);
return stringBuffer;
}
protected StringBuffer addQuotedTitle(StringBuffer stringBuffer) {
return addQuotedTitle(stringBuffer, "\"");
}
protected String getSubject() {
StringBuffer stringBuffer = new StringBuffer();
String fullName = ckanUser.getNameSurname();
if(!comment) {
switch (catalogueEventType) {
case ITEM_SUBMITTED:
stringBuffer.append(fullName);
stringBuffer.append(" created the item ");
break;
case ITEM_UPDATED:
stringBuffer.append(fullName);
stringBuffer.append(" updated the item ");
break;
case ITEM_REJECTED:
case ITEM_PUBLISHED:
addUserWithRole(fullName, Moderated.CATALOGUE_MODERATOR, stringBuffer, true);
stringBuffer.append(cmItemStatus.getValue());
stringBuffer.append(" the item ");
break;
default:
break;
}
}else {
addUserWithRole(fullName, itemAuthor ? SocialNotificationModerationThread.AUTHOR : Moderated.CATALOGUE_MODERATOR, stringBuffer, true);
stringBuffer.append("commented on the item ");
}
stringBuffer = addQuotedTitle(stringBuffer);
return stringBuffer.toString();
}
protected CatalogueEvent getCatalogueEvent(String messageString) throws Exception {
CatalogueEvent catalogueEvent = new CatalogueEvent();
catalogueEvent.setType(catalogueEventType);
catalogueEvent.setNotifyText(messageString);
catalogueEvent.setItemId(getSubject());
if(cmItemStatus == CMItemStatus.APPROVED) {
catalogueEvent.setItemURL(new URL(itemURL));
}else {
catalogueEvent.setItemURL(new URL(getModerationURL()));
}
Set<String> users = SocialUsers.getUsernamesByRole(Moderated.CATALOGUE_MODERATOR);
// Adding current ckanUser
users.add(CKANUser.getUsernameFromCKANUsername(ckanUser.getName()));
if(itemAuthorCkanUsername!=null) {
// Adding item author
users.add(CKANUser.getUsernameFromCKANUsername(itemAuthorCkanUsername));
}
catalogueEvent.setIdsToNotify(users.toArray(new String[users.size()]));
catalogueEvent.setIdsAsGroup(false);
return catalogueEvent;
}
@Override
protected void postMessage(String messageString) throws Exception {
CatalogueEvent catalogueEvent = getCatalogueEvent(messageString);
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
if(notificationSentByGCat) {
secretManager.startSession(secret);
}
try {
sendNotification(catalogueEvent);
}finally {
if(notificationSentByGCat) {
secretManager.endSession();
}
}
}
@Override
public void postUserMessage(CMItemStatus cmItemStatus, String userMessage) throws Exception {
/*
* [mister x] ([Role]) commented on the item "[TITLE]" as follows "[MESSAGE]". [Go to catalogue]
*/
this.create = false;
this.cmItemStatus = cmItemStatus;
this.comment = true;
switch (cmItemStatus) {
case PENDING:
catalogueEventType = CatalogueEventType.ITEM_UPDATED;
break;
case APPROVED:
catalogueEventType = CatalogueEventType.ITEM_PUBLISHED;
break;
case REJECTED:
catalogueEventType = CatalogueEventType.ITEM_REJECTED;
break;
default:
break;
}
String fullName = ckanUser.getNameSurname();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer = addUserWithRole(fullName, itemAuthor ? SocialNotificationModerationThread.AUTHOR : Moderated.CATALOGUE_MODERATOR, stringBuffer);
stringBuffer.append("commented on the item ");
stringBuffer = addQuotedTitle(stringBuffer);
stringBuffer.append(" as follows \"");
stringBuffer.append(userMessage);
stringBuffer.append("\".");
CatalogueEvent catalogueEvent = getCatalogueEvent(stringBuffer.toString());
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
if(notificationSentByGCat) {
secretManager.startSession(secret);
}
try {
sendNotification(catalogueEvent);
}finally {
if(notificationSentByGCat) {
secretManager.endSession();
}
}
}
protected void sendNotification(CatalogueEvent catalogueEvent) throws Exception {
Thread thread = new Thread() {
public void run() {
try {
logger.trace("{} is going to send the following notification {}", SecretManagerProvider.instance.get().getUser().getUsername(), catalogueEvent);
NotificationClient nc = new NotificationClient();
nc.sendCatalogueEvent(catalogueEvent);
} catch(Exception e) {
logger.error("Error while sending notification.", e);
}
}
};
thread.run();
// thread.start();
}
@Override
protected void createModerationThread() throws Exception {
create = true;
cmItemStatus = CMItemStatus.PENDING;
}
}

View File

@ -1,43 +0,0 @@
package org.gcube.gcat.moderation.thread.zulip;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ZulipAuth {
public static final String ZULIP_RC_FILENAME = "zuliprc";
public static final String EMAIL_KEY = "email";
public static final String KEY_KEY = "key";
public static final String SITE_KEY = "site";
protected final Properties properties;
public ZulipAuth(String username) {
properties = new Properties();
InputStream input = ZulipAuth.class.getClassLoader().getResourceAsStream(username+"_"+ZULIP_RC_FILENAME);
try {
// load the properties file
properties.load(input);
} catch(IOException e) {
throw new RuntimeException(e);
}
}
public String getEmail() {
return properties.getProperty(EMAIL_KEY);
}
public String getAPIKey() {
return properties.getProperty(KEY_KEY);
}
public String getSite() {
return properties.getProperty(SITE_KEY);
}
}

View File

@ -1,57 +0,0 @@
package org.gcube.gcat.moderation.thread.zulip;
import java.io.IOException;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ZulipResponse {
public static final String RESULT_KEY = "result";
public static final String MSG_KEY = "msg";
public enum Result {
success,
error
}
protected ObjectMapper objectMapper;
protected String responseString;
protected JsonNode response;
protected Result result;
protected String message;
public ZulipResponse(String responseString) {
this.responseString = responseString;
this.objectMapper = new ObjectMapper();
}
public Result getResponseResult() throws JsonProcessingException, IOException {
if(result==null) {
String resultString = getResponse().get(RESULT_KEY).asText();
result = Result.valueOf(resultString);
}
return result;
}
public String getResponseMessage() throws JsonProcessingException, IOException {
if(message==null) {
message = getResponse().get(MSG_KEY).asText();
}
return message;
}
public JsonNode getResponse() throws JsonProcessingException, IOException {
if(response == null) {
response = objectMapper.readTree(responseString);
}
return response;
}
}

View File

@ -1,171 +0,0 @@
package org.gcube.gcat.moderation.thread.zulip;
//import java.util.Set;
//
//import javax.ws.rs.InternalServerErrorException;
//
//import org.gcube.com.fasterxml.jackson.databind.JsonNode;
//import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
//import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
//import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
//import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.gcat.api.moderation.CMItemStatus;
//import org.gcube.gcat.api.moderation.Moderated;
import org.gcube.gcat.moderation.thread.ModerationThread;
//import org.gcube.gcat.moderation.thread.zulip.ZulipResponse.Result;
//import org.gcube.gcat.social.SocialUsers;
//import org.gcube.gcat.utils.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//import io.taliox.zulip.ZulipRestExecutor;
//import io.taliox.zulip.calls.ZulipRestAPICall;
//import io.taliox.zulip.calls.messages.PostMessage;
//import io.taliox.zulip.calls.streams.GetStreamID;
//import io.taliox.zulip.calls.streams.PostCreateStream;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ZulipStream extends ModerationThread {
private static final Logger logger = LoggerFactory.getLogger(ZulipStream.class);
@Override
protected void postMessage(String message) throws Exception {
logger.info("gCat is sending a message to the {} for item '{}' (id={}). ItemStatus={}, Message=\"{}\"",
ZulipStream.class.getSimpleName(), itemName, itemID, cmItemStatus, message);
}
@Override
public void postUserMessage(CMItemStatus cmItemStatus, String userMessage) throws Exception {
logger.info("{} is sending a message to the {} for item '{}' (id={}). ItemStatus={}, Message=\"{}\"",
SecretManagerProvider.instance.get().getUser().getUsername(),
ZulipStream.class.getSimpleName(), itemName, itemID, cmItemStatus, userMessage);
}
@Override
protected void createModerationThread() throws Exception {
logger.info("Creating {} for item '{}' (id={})", ZulipStream.class.getSimpleName(), itemName, itemID);
}
// public static final String TOPICS_KEY = "topics";
// public static final String NAME_KEY = "name";
// public static final String MAX_ID_KEY = "max_id";
// public static final String INITIAL_TOPIC_NAME = "hello";
//
// protected ZulipRestExecutor gCatZulipRestExecutor;
// protected ZulipRestExecutor userZulipRestExecutor;
//
// protected String streamName;
// protected String streamDescription;
//
// public ZulipStream() {
// super();
// }
//
// protected ZulipRestExecutor getZulipRestExecutor() {
// ZulipAuth zulipAuth = new ZulipAuth(SecretManagerProvider.instance.get().getUser().getUsername());
// return new ZulipRestExecutor(zulipAuth.getEmail(), zulipAuth.getAPIKey(), zulipAuth.getSite());
// }
//
// public ZulipRestExecutor getGCatZulipRestExecutor() throws Exception {
// if(gCatZulipRestExecutor==null) {
// SecretManager secretManager = SecretManagerProvider.instance.get();
// Secret secret = Constants.getCatalogueSecret();
// secretManager.startSession(secret);
// gCatZulipRestExecutor = getZulipRestExecutor();
// secretManager.endSession();
// }
// return gCatZulipRestExecutor;
// }
//
// public ZulipRestExecutor getUserZulipRestExecutor() {
// if(userZulipRestExecutor==null) {
// userZulipRestExecutor = getZulipRestExecutor();
// }
// return userZulipRestExecutor;
// }
//
// protected String getStreamName() {
// if(streamName==null) {
// streamName = String.format("Item '%s' moderation", itemID);
// }
// return streamName;
// }
//
// protected Integer getStreamID() throws Exception {
// GetStreamID getStreamID = new GetStreamID(getStreamName());
// ZulipResponse zulipResponse = executeZulipCall(gCatZulipRestExecutor, getStreamID);
// JsonNode response = zulipResponse.getResponse();
// return response.get("stream_id").asInt();
// }
//
// protected String getStreamDescription() {
// if(streamDescription==null) {
// streamDescription = String.format("This stream is used to discuss about the moderation of the item '%s' with id '%s'", itemName, itemID);
// }
// return streamDescription;
// }
//
// protected ZulipResponse executeZulipCall(ZulipRestExecutor zulipRestExecutor, ZulipRestAPICall call) throws Exception {
// logger.trace("Going to execute {}", call);
// String responseString = zulipRestExecutor.executeCall(call);
// logger.trace("Response from {} is {}", call.getClass().getSimpleName(), responseString);
// ZulipResponse zulipResponse = new ZulipResponse(responseString);
// if(zulipResponse.getResponseResult()==Result.error) {
// throw new InternalServerErrorException(zulipResponse.getResponseMessage());
// }
// return zulipResponse;
// }
//
// @Override
// protected void createModerationThread() throws Exception {
// ArrayNode streamsArrayNode = objectMapper.createArrayNode();
// ObjectNode streamobjectNode = objectMapper.createObjectNode();
// streamobjectNode.put("name", getStreamName());
// streamobjectNode.put("description", getStreamDescription());
// streamsArrayNode.add(streamobjectNode);
//
// ArrayNode principalsArrayNode = objectMapper.createArrayNode();
// // Going to add the item creator
// String itemCreatorEmail = ckanUser.getEMail();
// principalsArrayNode.add(itemCreatorEmail);
//
// getGCatZulipRestExecutor();
//
// principalsArrayNode.add(gCatZulipRestExecutor.httpController.getUserName());
//
// // Going to add the catalogue moderators
// Set<String> moderators = SocialUsers.getUsernamesByRole(Moderated.CATALOGUE_MODERATOR);
// for(String moderator : moderators) {
// principalsArrayNode.add(moderator);
// }
//
// PostCreateStream postCreateStream = new PostCreateStream(streamsArrayNode.toString());
// postCreateStream.setPrincipals(principalsArrayNode.toString());
// postCreateStream.setInvite_only(true);
// postCreateStream.setAnnounce(false);
//
// executeZulipCall(gCatZulipRestExecutor, postCreateStream);
// }
//
// protected void postMessageToStream(ZulipRestExecutor zulipRestExecutor, String message) throws Exception {
// PostMessage postMessage = new PostMessage(getStreamName(), cmItemStatus.getFancyValue(), message);
// logger.debug("Going to send the following message: {}", message);
// executeZulipCall(zulipRestExecutor, postMessage);
// }
//
// @Override
// protected void postMessage(String message) throws Exception {
// postMessageToStream(getGCatZulipRestExecutor(), message);
// }
//
// @Override
// public void postUserMessage(CMItemStatus cmItemStatus, String message) throws Exception {
// this.cmItemStatus = cmItemStatus;
// postMessageToStream(getUserZulipRestExecutor(), message);
// }
}

View File

@ -1,131 +0,0 @@
package org.gcube.gcat.oldutils;
import org.gcube.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;
}
}
}

View File

@ -1,645 +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.lang3.math.NumberUtils;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
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.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.MetadataVocabulary;
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.geojson.GeoJsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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;
public static final String ITEM_URL = "Item URL";
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(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;
Map<String, MetadataField> metadataFieldMap = new HashMap<>();
for(MetadataField metadataField : metadataFields) {
metadataFieldMap.put(metadataField.getFieldName(), metadataField);
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 upperBound = Integer.MAX_VALUE;
try {
String maxOccurs = metadataFieldMap.get(entry.getKey()).getMaxOccurs();
if(maxOccurs==null || maxOccurs.compareTo("*")==0) {
upperBound = Integer.MAX_VALUE;
}else {
try {
upperBound = Integer.valueOf(maxOccurs);
}catch (Exception e) {
}
}
}catch (Exception e) {
upperBound = Integer.MAX_VALUE;
}
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 = false;
MetadataVocabulary metadataVocabulary = metadataField.getVocabulary();
if(metadataVocabulary!=null) {
List<String> vocabularyFields = metadataVocabulary.getVocabularyFields();
if(vocabularyFields!=null && vocabularyFields.size()>0) {
hasControlledVocabulary = true;
}
}
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.isCreatable(value))
throw new BadRequestException("Field's value with key '" + key + "' is not a valid number!");
break;
case GeoJSON:
try {
new com.fasterxml.jackson.databind.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();
}
}

View File

@ -1,367 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import java.io.IOException;
import java.io.InputStream;
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.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.NullNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
import org.gcube.gcat.utils.HTTPUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @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 TITLE_KEY = "title";
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 = CKANUtility.getCkanURL();
GXHTTPStringRequest gxhttpStringRequest = HTTPUtility.createGXHTTPStringRequest(catalogueURL, path, post);
gxhttpStringRequest.isExternalCall(true);
gxhttpStringRequest.header(AUTH_CKAN_HEADER, getApiKey());
return gxhttpStringRequest;
}
protected String getResultAsString(HttpURLConnection httpURLConnection) throws IOException {
int responseCode = httpURLConnection.getResponseCode();
if(responseCode >= Status.BAD_REQUEST.getStatusCode()) {
Status status = Status.fromStatusCode(responseCode);
switch (status) {
case NOT_FOUND:
throw new NotFoundException();
default:
break;
}
InputStream inputStream = httpURLConnection.getErrorStream();
StringBuilder result = HTTPUtility.getStringBuilder(inputStream);
logger.trace(result.toString());
try {
JsonNode jsonNode = getAsJsonNode(result.toString());
JsonNode error = jsonNode.get(ERROR_KEY);
throw new WebApplicationException(getAsString(error), status);
}catch (WebApplicationException e) {
throw e;
}catch (Exception e) {
throw new WebApplicationException(result.toString(), status);
}
}
InputStream inputStream = httpURLConnection.getInputStream();
String ret = HTTPUtility.getStringBuilder(inputStream).toString();
logger.trace("Got Respose is {}", ret);
return ret;
}
protected String getResultAndValidate(HttpURLConnection httpURLConnection) throws IOException {
String ret = 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();
}
}
public void purge() {
sendPostRequest(PURGE, createJsonNodeWithNameAsID());
}
}

View File

@ -1,108 +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 org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANGroup extends CKAN {
// see https://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 https://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 https://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 https://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 https://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 https://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 https://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;
}
public 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;
}
public int count() {
list(100000, 0);
ArrayNode arrayNode = (ArrayNode) result;
return arrayNode.size();
}
}

View File

@ -1,49 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANLicense extends CKAN {
// see https://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;
}
}
}

View File

@ -1,86 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @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 https://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 https://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 https://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 https://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 https://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 https://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 https://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 = SecretManagerProvider.instance.get().getContext();
return getCKANOrganizationName(context);
}
public static String getCKANOrganizationName(String context) {
ScopeBean scopeBean = new ScopeBean(context);
return scopeBean.name().toLowerCase();
}
public int count() {
list(100000, 0);
ArrayNode arrayNode = (ArrayNode) result;
return arrayNode.size();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,218 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
import java.util.concurrent.TimeUnit;
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.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.gcat.api.configuration.CKANDB;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.api.roles.Role;
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
import org.postgresql.core.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANPackageTrash {
protected static final Logger logger = LoggerFactory.getLogger(CKANPackageTrash.class);
private static final String GROUP_TABLE_KEY = "group";
private static final String GROUP_ID_KEY = "id";
private static final String GROUP_NAME_KEY = "name";
private static final String PACKAGE_TABLE_KEY = "package";
private static final String PACKAGE_NAME_KEY = "name";
private static final String PACKAGE_TYPE_KEY = "type";
private static final String PACKAGE_TYPE_VALUE = "dataset";
private static final String PACKAGE_STATE_KEY = "state";
private static final String PACKAGE_STATE_VALUE = "deleted";
private static final String PACKAGE_OWNER_ORG_KEY = "owner_org";
protected ObjectMapper mapper;
protected final CKANUser ckanUser;
protected final CatalogueConfiguration configuration;
protected final Set<String> supportedOrganizations;
protected boolean ownOnly;
public CKANPackageTrash() {
mapper = new ObjectMapper();
ckanUser = CKANUserCache.getCurrrentCKANUser();
configuration = CatalogueConfigurationFactory.getInstance();
supportedOrganizations = configuration.getSupportedOrganizations();
ownOnly = true;
}
public void setOwnOnly(boolean ownOnly) {
this.ownOnly = ownOnly;
}
protected Connection getConnection() throws Exception {
Class.forName("org.postgresql.Driver");
CKANDB ckanDB = configuration.getCkanDB();
String url = ckanDB.getUrl();
String username = ckanDB.getUsername();
String password = ckanDB.getPassword();
Connection connection = DriverManager.getConnection(url, username, password);
logger.trace("Database {} opened successfully", url);
connection.setAutoCommit(false);
return connection;
}
protected String getQuotedString(String string) throws SQLException {
StringBuilder builder = new StringBuilder();
builder.append("'");
Utils.escapeLiteral(builder, string, false);
builder.append("'");
return builder.toString();
}
protected ArrayNode getItems() throws WebApplicationException {
Connection connection = null;
try {
StringBuffer stringBufferOrg = new StringBuffer();
stringBufferOrg.append("SELECT ");
stringBufferOrg.append(GROUP_ID_KEY);
stringBufferOrg.append(" FROM \"");
stringBufferOrg.append(GROUP_TABLE_KEY);
stringBufferOrg.append("\" WHERE ");
stringBufferOrg.append(GROUP_NAME_KEY);
stringBufferOrg.append(" IN ");
stringBufferOrg.append("(");
boolean first = true;
for(String organizationName : supportedOrganizations) {
if(first) {
first = false;
}else {
stringBufferOrg.append(",");
}
stringBufferOrg.append(getQuotedString(organizationName));
}
stringBufferOrg.append(")");
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("SELECT ");
stringBuffer.append(PACKAGE_NAME_KEY);
stringBuffer.append(" FROM ");
stringBuffer.append(PACKAGE_TABLE_KEY);
stringBuffer.append(" WHERE ");
stringBuffer.append(PACKAGE_TYPE_KEY);
stringBuffer.append("=");
stringBuffer.append(getQuotedString(PACKAGE_TYPE_VALUE));
stringBuffer.append(" AND ");
stringBuffer.append(PACKAGE_STATE_KEY);
stringBuffer.append("=");
stringBuffer.append(getQuotedString(PACKAGE_STATE_VALUE));
if(ownOnly || ckanUser.getRole().ordinal()<Role.ADMIN.ordinal()) {
// add only own items
stringBuffer.append(" AND ");
stringBuffer.append(CKANPackage.AUTHOR_EMAIL_KEY);
stringBuffer.append("=");
stringBuffer.append(getQuotedString(ckanUser.getEMail()));
}
stringBuffer.append(" AND ");
stringBuffer.append(PACKAGE_OWNER_ORG_KEY);
stringBuffer.append(" IN (");
stringBuffer.append(stringBufferOrg);
stringBuffer.append(")");
ArrayNode items = mapper.createArrayNode();
connection = getConnection();
Statement statement = connection.createStatement();
String sql = stringBuffer.toString();
logger.trace("Going to request the following query: {}", sql);
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
String id = resultSet.getString(PACKAGE_NAME_KEY);
items.add(id);
}
return items;
} catch (WebApplicationException e) {
throw e;
} catch (Exception e) {
throw new WebApplicationException(e);
}finally {
if(connection!=null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
}
public String list() throws WebApplicationException {
try {
return mapper.writeValueAsString(getItems());
} catch (WebApplicationException e) {
throw e;
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
public ObjectNode removeAll() throws WebApplicationException {
ObjectNode objectNode = mapper.createObjectNode();
ArrayNode deleted = mapper.createArrayNode();
ArrayNode notDeleted = mapper.createArrayNode();
ArrayNode itemNames = getItems();
CKANPackage ckanPackage = new CKANPackage();
for(int i=0; i<itemNames.size(); i++) {
String name = itemNames.get(i).asText();
try {
ckanPackage.reuseInstance();
ckanPackage.setName(name);
ckanPackage.purge();
deleted.add(name);
}catch (Exception e) {
notDeleted.add(name);
}
try {
Thread.sleep(TimeUnit.MILLISECONDS.toMillis(300));
} catch (InterruptedException e) {
}
}
objectNode.set("deleted", deleted);
objectNode.set("failed", notDeleted);
return objectNode;
}
public String empty() throws WebApplicationException {
try {
return mapper.writeValueAsString(removeAll());
} catch (WebApplicationException e) {
throw e;
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
}

View File

@ -1,522 +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.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
import org.gcube.gcat.utils.Constants;
import org.gcube.gcat.utils.HTTPCall;
import org.gcube.gcat.workspace.CatalogueStorageHubManagement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANResource extends CKAN {
private static final Logger logger = LoggerFactory.getLogger(CKANResource.class);
// see https://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 https://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 https://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 https://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 https://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.dev.d4science.org";
public static final String URI_RESOLVER_STORAGE_HUB_HOST;
public static final String URI_RESOLVER_STORAGE_HUB_PATH = "/shub/";
static {
String context = SecretManagerProvider.instance.get().getContext();
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
public 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();
}
}
}

View File

@ -1,332 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import java.util.Collection;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.user.User;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.api.moderation.Moderated;
import org.gcube.gcat.api.roles.Role;
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
import org.gcube.gcat.utils.RandomString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @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 https://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 https://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 https://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 https://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 static final String PORTAL_ROLES = "portal_roles";
protected Role role;
protected Boolean catalogueModerator;
public CKANUser() {
super();
LIST = USER_LIST;
CREATE = USER_CREATE;
READ = USER_SHOW;
UPDATE = USER_UPDATE;
PATCH = null;
DELETE = USER_DELETE;
PURGE = null;
catalogueModerator = null;
}
public void setName(String name) {
name = getCKANUsername(name);
this.name = name;
}
public String createInCkan() {
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 jobTitle = SecretManagerProvider.instance.get().getUser().getAbout();
String ckanJobTitle = "";
if(objectNode.has(ABOUT)) {
ckanJobTitle = objectNode.get(ABOUT).asText();
}
if(jobTitle!=null && jobTitle.compareTo(ckanJobTitle) != 0) {
objectNode.put(ABOUT, jobTitle);
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 = getSurnameName();
String ckanFullname = "";
if(objectNode.has(FULL_NAME)) {
ckanFullname = objectNode.get(FULL_NAME).asText();
}
if(portalFullname!=null && 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) {
User user = SecretManagerProvider.instance.get().getUser();
String portalEmail = user.getEmail();
String ckanEmail = "";
if(objectNode.has(EMAIL)) {
ckanEmail = objectNode.get(EMAIL).asText();
}
if(portalEmail==null) {
String username = user.getUsername();
String eMail = username + "@d4science.org";
objectNode.put(EMAIL, eMail);
return true;
} else 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) {
createInCkan();
} else {
throw e;
}
}
try {
CatalogueConfiguration configuration = CatalogueConfigurationFactory.getInstance();
for(String supportedOrganization : configuration.getSupportedOrganizations()) {
addUserToOrganization(supportedOrganization);
}
}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 String parseResult() {
name = result.get(NAME).asText();
// Only managers can read Ckan API key
if(getRole().ordinal()<Role.MANAGER.ordinal()) {
try {
apiKey = result.get(API_KEY).asText();
}catch (Exception e) {
// The user reading its own Ckan profile must be able to read its API key
if(name.compareTo(getCKANUsername())==0) {
throw e;
}
}
}else {
((ObjectNode) result).remove(API_KEY);
}
if(name.compareTo(getCKANUsername())==0) {
ArrayNode roles = ((ObjectNode) result).putArray(PORTAL_ROLES);
roles.add(getRole().getPortalRole());
if(isCatalogueModerator()) {
roles.add(Moderated.CATALOGUE_MODERATOR);
}
}
return getAsString(result);
}
protected static String getCKANUsername(String username) {
if(username == null) {
return null;
}
return username.trim().replaceAll("\\.", "_");
}
public static String getUsernameFromCKANUsername(String ckanUsername) {
if(ckanUsername == null) {
return null;
}
return ckanUsername.trim().replaceAll("_", ".");
}
public static String getCKANUsername() {
String username = SecretManagerProvider.instance.get().getUser().getUsername();
return getCKANUsername(username);
}
public String create(String json) {
super.create(json);
String ret = parseResult();
return ret;
}
public String read() {
super.read();
String ret = parseResult();
return ret;
}
public String update(String json) {
super.update(json);
String ret = parseResult();
return ret;
}
public 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;
Collection<String> roles = SecretManagerProvider.instance.get().getUser().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 boolean isCatalogueModerator() {
if(catalogueModerator == null) {
catalogueModerator = SecretManagerProvider.instance.get().getUser().getRoles().contains(Moderated.CATALOGUE_MODERATOR);
}
return catalogueModerator;
}
public String getSurnameName(){
User user = SecretManagerProvider.instance.get().getUser();
return user.getFullName();
}
public String getNameSurname() {
User user = SecretManagerProvider.instance.get().getUser();
return user.getFullName(true);
}
public String getEMail() {
User user = SecretManagerProvider.instance.get().getUser();
return user.getEmail();
}
}

View File

@ -1,82 +0,0 @@
package org.gcube.gcat.persistence.ckan;
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.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public abstract class CKANUserCache {
private static final CacheManager cacheManager;
private static final MutableConfiguration<String,CKANUser> userCacheConfiguration;
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)));
}
private CKANUserCache() {
}
public synchronized static CKANUser getCurrrentCKANUser() {
SecretManager secretManager = SecretManagerProvider.instance.get();
String context = secretManager.getContext();
Cache<String,CKANUser> userCache = cacheManager.getCache(context);
if(userCache == null) {
userCache = cacheManager.createCache(context, userCacheConfiguration);
}
String gcubeUsername = secretManager.getUser().getUsername();
CKANUser ckanUser = userCache.get(gcubeUsername);
if(ckanUser == null) {
ckanUser = new CKANUser();
ckanUser.retrieve();
userCache.put(gcubeUsername, ckanUser);
}
return ckanUser;
}
public synchronized static void removeUserFromCache() {
SecretManager secretManager = SecretManagerProvider.instance.get();
String gcubeUsername = secretManager.getUser().getUsername();
removeUserFromCache(gcubeUsername);
}
public synchronized static void removeUserFromCache(String gcubeUsername) {
SecretManager secretManager = SecretManagerProvider.instance.get();
String context = secretManager.getContext();
Cache<String,CKANUser> userCache = cacheManager.getCache(context);
if(userCache != null) {
userCache.remove(gcubeUsername);
}
}
public synchronized static void emptyUserCache() {
SecretManager secretManager = SecretManagerProvider.instance.get();
String context = secretManager.getContext();
cacheManager.destroyCache(context);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
cacheManager.close();
}
}

View File

@ -1,37 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import javax.ws.rs.InternalServerErrorException;
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANUtility {
public static String getCkanURL() {
try {
return CatalogueConfigurationFactory.getInstance().getCkanURL();
} catch(Exception e) {
throw new InternalServerErrorException(e);
}
}
public static String getSysAdminAPI() {
try {
return CatalogueConfigurationFactory.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);
}
}
}

View File

@ -1,21 +1,36 @@
package org.gcube.gcat.profile;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.List;
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.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
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.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.QueryForResourceUtil;
import org.gcube.gcat.api.roles.Role;
import org.gcube.gcat.persistence.ckan.CKANUser;
import org.gcube.gcat.persistence.ckan.CKANUserCache;
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;
@ -29,9 +44,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@ -169,7 +181,61 @@ 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 {
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
if(ckanUser.getRole().ordinal()<Role.EDITOR.ordinal()) {
@ -177,7 +243,12 @@ public class ISProfile {
}
MetadataUtility metadataUtility = new MetadataUtility();
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);
return true;
} else {

View File

@ -1,70 +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;
/**
* @author Luca Frosini (ISTI - CNR)
*/
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();
}
}

View File

@ -21,7 +21,7 @@ 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.CKANUserCache;
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;

View File

@ -95,7 +95,7 @@ public class Item extends REST<CKANPackage> implements org.gcube.gcat.api.interf
*
* <h4>Filtering options based on Solr query parameters</h4>
* <p>
* It accepts the following query parameters (a subset of Solr search query parameters, see Solr Query Syntax):
* 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>
@ -221,7 +221,7 @@ public class Item extends REST<CKANPackage> implements org.gcube.gcat.api.interf
* @responseExample application/json;charset=UTF-8 ["item0","items1",...,"item10"]
*/
@GET
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
@Produces({GCatConstants.APPLICATION_JSON_CHARSET_UTF_8, GCatConstants.APPLICATION_JSON_API})
@StatusCodes ({
@ResponseCode ( code = 200, condition = "The request succeeded.")
})
@ -238,17 +238,24 @@ public class Item extends REST<CKANPackage> implements org.gcube.gcat.api.interf
countOnly = Boolean.parseBoolean(queryParameters.get(GCatConstants.COUNT_QUERY_PARAMETER).get(0));
}
String ret = null;
if(countOnly) {
setCalledMethod("GET /" + COLLECTION_PARAMETER);
CKANPackage ckan = getInstance();
int size = ckan.count();
return createCountJson(size);
ret = createCountJson(size);
}else {
return super.list(limit, offset);
ret = super.list(limit, offset);
}
String accept = httpHeaders.getHeaderString("Accept");
if(accept!=null && accept.contains(GCatConstants.APPLICATION_JSON_API)) {
return resultAsJsonAPI(ret);
}
return ret;
}
/**
*
* The create API allows to create an item.

View File

@ -4,7 +4,9 @@ import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.xml.ws.WebServiceException;
import org.gcube.gcat.api.GCatConstants;
import org.gcube.gcat.persistence.ckan.CKANLicense;
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
@ -33,9 +35,15 @@ public class License extends REST<CKANLicense> implements org.gcube.gcat.api.int
* @responseExample application/json classpath:/api-docs-examples/license/list-license-response.json
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public String list() {
return super.list(-1, -1);
@Produces({MediaType.APPLICATION_JSON,GCatConstants.APPLICATION_JSON_API})
public String list() throws WebServiceException {
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;
}
}

View File

@ -98,4 +98,12 @@ public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Resp
return delete(id, true);
}
protected String resultAsJsonAPI(String data) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("{ \"data\":");
stringBuffer.append(data);
stringBuffer.append("}");
return stringBuffer.toString();
}
}

View File

@ -32,7 +32,7 @@ public class RequestFilter implements ContainerRequestFilter, ContainerResponseF
public void filter(ContainerRequestContext requestContext) throws IOException {
logger.trace("PreMatching RequestFilter");
SecretManagerProvider.instance.reset();
SecretManagerProvider.instance.remove();
SecretManager secretManager = new SecretManager();
String token = AccessTokenProvider.instance.get();
@ -48,14 +48,13 @@ public class RequestFilter implements ContainerRequestFilter, ContainerResponseF
}
SecretManagerProvider.instance.set(secretManager);
}
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
logger.trace("ResponseFilter");
SecretManagerProvider.instance.reset();
SecretManagerProvider.instance.remove();
}
}

View File

@ -31,9 +31,9 @@ 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.ServiceCatalogueConfiguration;
import org.gcube.gcat.configuration.service.ServiceCatalogueConfiguration;
import org.gcube.gcat.persistence.ckan.CKANUser;
import org.gcube.gcat.persistence.ckan.CKANUserCache;
import org.gcube.gcat.persistence.ckan.cache.CKANUserCache;
import org.gcube.gcat.rest.BaseREST;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -222,7 +222,7 @@ public class Configuration extends BaseREST implements org.gcube.gcat.api.interf
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
public Response create(String json) throws WebServiceException {
try {
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json);
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json, ServiceCatalogueConfiguration.class);
checkContext(CURRENT_CONTEXT_PATH_PARAMETER, catalogueConfiguration);
String ret = createOrUpdate(catalogueConfiguration);
ResponseBuilder responseBuilder = Response.status(Status.CREATED);
@ -352,7 +352,7 @@ public class Configuration extends BaseREST implements org.gcube.gcat.api.interf
@OperationId("Create or Update")
public String createOrUpdate(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException {
try {
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json);
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json, ServiceCatalogueConfiguration.class);
checkContext(context, catalogueConfiguration);
return createOrUpdate(catalogueConfiguration);
}catch (WebApplicationException e) {
@ -365,7 +365,7 @@ public class Configuration extends BaseREST implements org.gcube.gcat.api.interf
@Override
public Response update(String json) throws WebServiceException {
try {
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json);
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json, ServiceCatalogueConfiguration.class);
checkContext(CURRENT_CONTEXT_PATH_PARAMETER);
catalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(catalogueConfiguration);
String configuration = catalogueConfiguration.toJsonString();
@ -451,7 +451,7 @@ public class Configuration extends BaseREST implements org.gcube.gcat.api.interf
configuration.set(fieldName, node.get(fieldName));
}
ServiceCatalogueConfiguration newCatalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(configuration);
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);

View File

@ -21,7 +21,7 @@ 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.CKANUserCache;
import org.gcube.gcat.persistence.ckan.cache.CKANUserCache;
import org.gcube.gcat.rest.REST;
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
@ -141,13 +141,8 @@ public class User extends REST<CKANUser> implements org.gcube.gcat.api.interface
@DELETE
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
public Response removeCallerFromCache() {
SecretManager secretManager = SecretManagerProvider.instance.get();
org.gcube.common.authorization.utils.user.User user = secretManager.getUser();
if(user.getRoles().contains(Role.MANAGER.getPortalRole())) {
CKANUserCache.removeUserFromCache();
}else {
throw new ForbiddenException("Only " + Role.MANAGER.getPortalRole() + "s are authorized to remove an user from the cache");
}
// Any user can remove self from cache
CKANUserCache.removeUserFromCache();
return Response.status(Status.NO_CONTENT).build();
}
@ -157,7 +152,8 @@ public class User extends REST<CKANUser> implements org.gcube.gcat.api.interface
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())) {
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");

View File

@ -1,172 +0,0 @@
package org.gcube.gcat.social;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
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.Secret;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
import org.gcube.gcat.persistence.ckan.CKANUserCache;
import org.gcube.gcat.utils.Constants;
import org.gcube.portal.databook.shared.Post;
import org.gcube.social_networking.social_networking_client_library.PostClient;
import org.gcube.social_networking.socialnetworking.model.beans.PostInputBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class SocialPost extends Thread {
private static final Logger logger = LoggerFactory.getLogger(SocialPost.class);
protected static final String NOTIFICATION_MESSAGE = "%s just published the item \"%s\"\n"
+ "Please find it at %s\n";
protected String itemID;
protected String itemURL;
protected String itemTitle;
protected List<String> tags;
protected Boolean notification;
public SocialPost() throws Exception {
super();
}
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);
}
}
}
public Boolean isNotification() {
return notification;
}
public void setNotification(Boolean notification) {
this.notification = notification;
}
@Override
public void run() {
try {
CatalogueConfiguration instance = CatalogueConfigurationFactory.getInstance();
if(!instance.isSocialPostEnabled()) {
logger.info("Social Post are disabled in the context {}", SecretManagerProvider.instance.get().getContext());
return;
}
logger.info("Going to send Social Post about the Item {} available at {}", itemID, itemURL);
boolean notifyUsers = instance.isNotificationToUsersEnabled();
if(notification != null) {
if(notifyUsers) {
notifyUsers = notifyUsers && notification;
}else {
notifyUsers = notification;
}
}
// write notification post
sendSocialPost(notifyUsers);
} catch(Exception e) {
logger.error("Error while executing post creation actions", e);
}
}
public void sendSocialPost(boolean notifyUsers) {
SecretManager secretManager = SecretManagerProvider.instance.get();
try {
String fullName = CKANUserCache.getCurrrentCKANUser().getNameSurname();
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 social post that is going to be written is\n{}", message);
Secret secret = Constants.getCatalogueSecret();
secretManager.startSession(secret);
PostClient postClient = new PostClient();
PostInputBean postInputBean = new PostInputBean();
postInputBean.setEnablenotification(notifyUsers);
postInputBean.setText(message);
Post post = postClient.writeApplicationPost(postInputBean);
logger.trace("Sent post {}", post);
} catch(Exception e) {
logger.error("Unable to send Social Post", e);
} finally {
secretManager.endSession();
}
}
}

View File

@ -1,20 +0,0 @@
package org.gcube.gcat.social;
import java.util.HashSet;
import java.util.Set;
import org.gcube.social_networking.social_networking_client_library.UserClient;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class SocialUsers {
public static Set<String> getUsernamesByRole(String roleName) throws Exception {
UserClient userClient = new UserClient();
Set<String> usernames = new HashSet<>(userClient.getAllUsernamesByLocalRole(roleName));
return usernames;
}
}

View File

@ -1,54 +0,0 @@
package org.gcube.gcat.utils;
import java.io.InputStream;
import java.util.Properties;
import javax.ws.rs.InternalServerErrorException;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.JWTSecret;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.keycloak.model.TokenResponse;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class Constants {
public static final String CATALOGUE_NAME = "gCat";
private static final String CLIENT_SECRET_FILENAME = "config.properties";
private static final String CLIENT_ID = "gcat";
protected static String clientSecret;
private static String getClientSecret(String context) {
try {
if(clientSecret==null) {
Properties properties = new Properties();
InputStream input = Constants.class.getClassLoader().getResourceAsStream(CLIENT_SECRET_FILENAME);
properties.load(input);
int index = context.indexOf('/', 1);
String root = context.substring(0, index == -1 ? context.length() : index);
clientSecret = properties.getProperty(root);
}
return clientSecret;
} catch(Exception e) {
throw new InternalServerErrorException(
"Unable to retrieve Application Token for context " + SecretManagerProvider.instance.get().getContext(), e);
}
}
private static TokenResponse getJWTAccessToken() throws Exception {
String contextToAuthorise = SecretManagerProvider.instance.get().getContext();
TokenResponse tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, getClientSecret(contextToAuthorise), contextToAuthorise, null);
return tr;
}
public static Secret getCatalogueSecret() throws Exception {
TokenResponse tr = getJWTAccessToken();
Secret secret = new JWTSecret(tr.getAccessToken());
return secret;
}
}

View File

@ -1,90 +0,0 @@
package org.gcube.gcat.utils;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
/**
* @author Luca Frosini (ISTI - CNR)
*/
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.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(1));
// 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);
}
}
}

View File

@ -1,65 +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;
/**
* @author Luca Frosini (ISTI - CNR)
*/
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(result.toString(), status);
}
InputStream inputStream = httpURLConnection.getInputStream();
String ret = getStringBuilder(inputStream).toString();
logger.trace("Got Respose is {}", ret);
return ret;
}
}

View File

@ -1,37 +0,0 @@
package org.gcube.gcat.utils;
import java.util.Random;
/**
* @author Lucio Lelii (ISTI - CNR)
* @author Luca Frosini (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);
}
}

View File

@ -1,40 +0,0 @@
package org.gcube.gcat.utils;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.portlets.user.uriresolvermanager.UriResolverManager;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public abstract 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";
public static String getCatalogueItemURL(String name) {
try {
String context = SecretManagerProvider.instance.get().getContext();
UriResolverManager uriResolverManager = new UriResolverManager("CTLG");
Map<String, String> params = new HashMap<>();
params.put(CATALOGUE_CONTEXT, context);
params.put(ENTITY_TYPE, DATASET);
params.put(ENTITY_NAME, name);
String url = uriResolverManager.getLink(params, false);
return url;
} catch(WebApplicationException e) {
throw e;
} catch(Exception e) {
throw new WebApplicationException(e);
}
}
}

View File

@ -1,48 +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;
/**
* @author Luca Frosini (ISTI - CNR)
*/
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;
}
}

View File

@ -1,146 +0,0 @@
package org.gcube.gcat.workspace;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.Secret;
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.StorageHubManagement;
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
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 Exception {
String contentDisposition = httpURLConnection.getHeaderField("Content-Disposition");
contentDisposition = contentDisposition.replaceAll("= ", "=").replaceAll(" =", "=");
ContentDisposition formDataContentDisposition = new ContentDisposition(contentDisposition);
return formDataContentDisposition.getFileName();
}
public URL ensureResourcePersistence(URL persistedURL, String itemID, String resourceID) throws Exception {
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
try {
secretManager.startSession(secret);
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;
} catch (Exception e) {
logger.error("Error while trying to persists the resource", e);
throw e;
} finally {
secretManager.endSession();
}
}
public void deleteResourcePersistence(String itemID, String resourceID, String mimeType) throws Exception {
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
try {
secretManager.startSession(secret);
storageHubManagement = new StorageHubManagement();
CatalogueMetadata catalogueMetadata = new CatalogueMetadata(itemID);
storageHubManagement.setCheckMetadata(catalogueMetadata);
storageHubManagement.removePersistedFile(resourceID, mimeType);
} finally {
secretManager.endSession();
}
}
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 {
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
try {
secretManager.startSession(secret);
FileContainer createdfile = storageHubManagement.getPersistedFile();
createdfile.rename(resourceID);
internalAddRevisionID(resourceID, revisionID);
} finally {
secretManager.endSession();
}
}
public void addRevisionID(String resourceID, String revisionID) throws Exception {
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
try {
secretManager.startSession(secret);
internalAddRevisionID(resourceID, revisionID);
} finally {
secretManager.endSession();
}
}
public FileContainer retrievePersistedFile(String id, String mimeType) throws Exception {
SecretManager secretManager = SecretManagerProvider.instance.get();
Secret secret = Constants.getCatalogueSecret();
try {
secretManager.startSession(secret);
return storageHubManagement.getPersistedFile(id, mimeType);
} finally {
secretManager.endSession();
}
}
public FileContainer getPersistedFile() {
return storageHubManagement.getPersistedFile();
}
}

View File

@ -28,5 +28,5 @@
"fullname": "Brown Mister",
"id": "7445ef41-08d8-4559-8750-3232eb0e1914",
"number_created_packages": 0
},
}
]

View File

@ -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"
}
}
}

View File

@ -0,0 +1,13 @@
{
"type": "VirtualService",
"consistsOf": [
{
"type": "IsIdentifiedBy",
"target": {
"type": "SoftwareFacet",
"group": "org.gcube.data-catalogue",
"name": "catalogue-virtual-service"
}
}
]
}

View File

@ -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"
}
}
]
}
}
]
}

View File

@ -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"
}
}
]
}
}
]
}
}
}

View File

@ -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"
}
}
]
}
}
]
}

View File

@ -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"
}
}
]
}
}
]
}
}
}

View File

@ -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"
}
}
]
}
}
]
}
}
]
}

View File

@ -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"
}
}
]
}
}
]
}
}
]
}
}
}

View File

@ -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"
}
}
]
}
}
]
}
}
]
}

View File

@ -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"
}
}
]
}
}
]
}
}
]
}
}
}

View File

@ -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 |
| |
--------------------

View File

@ -5,13 +5,13 @@
<servlet-name>org.gcube.gcat.ResourceInitializer</servlet-name>
</servlet>
<servlet-mapping>
2 <servlet-name>default</servlet-name>
3 <url-pattern>/docs/*</url-pattern>
4 </servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/docs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
2 <servlet-name>default</servlet-name>
3 <url-pattern>/api-docs/*</url-pattern>
4 </servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/api-docs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>org.gcube.gcat.ResourceInitializer</servlet-name>
<url-pattern>/*</url-pattern>

View File

@ -9,76 +9,177 @@ import java.util.Properties;
import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.JWTSecret;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.common.authorization.utils.secret.SecretUtility;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.keycloak.KeycloakClientHelper;
import org.gcube.common.keycloak.model.TokenResponse;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ContextTest {
protected static Properties properties;
protected static final String PROPERTIES_FILENAME = "token.properties";
private static final Logger logger = LoggerFactory.getLogger(ContextTest.class);
protected static final String CONFIG_INI_FILENAME = "config.ini";
public static final String DEFAULT_TEST_SCOPE;
public static final String GCUBE;
public static final String DEVNEXT;
public static final String NEXTNEXT;
public static final String DEVSEC;
public static final String DEVVRE;
public static final String ROOT;
public static final String VO;
public static final String VRE;
private static final String ROOT_PRE;
private static final String VO_PREPROD;
protected static final String VRE_GRSF_PRE;
private static final String ROOT_PROD;
protected static final Properties properties;
public static final String TYPE_PROPERTY_KEY = "type";
public static final String USERNAME_PROPERTY_KEY = "username";
public static final String PASSWORD_PROPERTY_KEY = "password";
public static final String CLIENT_ID_PROPERTY_KEY = "clientId";
static {
properties = new Properties();
InputStream input = ContextTest.class.getClassLoader().getResourceAsStream(PROPERTIES_FILENAME);
GCUBE = "/gcube";
DEVNEXT = GCUBE + "/devNext";
NEXTNEXT = DEVNEXT + "/NextNext";
DEVSEC = GCUBE + "/devsec";
DEVVRE = DEVSEC + "/devVRE";
ROOT = GCUBE;
VO = DEVSEC;
VRE = DEVVRE;
ROOT_PRE = "/pred4s";
VO_PREPROD = ROOT_PRE + "/preprod";
VRE_GRSF_PRE = VO_PREPROD + "/GRSF_Pre";
ROOT_PROD = "/d4science.research-infrastructures.eu";
DEFAULT_TEST_SCOPE = VRE;
// DEFAULT_TEST_SCOPE = VRE_GRSF_PRE;
properties = new Properties();
InputStream input = ContextTest.class.getClassLoader().getResourceAsStream(CONFIG_INI_FILENAME);
try {
// load the properties file
properties.load(input);
} catch(IOException e) {
} catch (IOException e) {
throw new RuntimeException(e);
}
// DEFAULT_TEST_SCOPE_NAME = "/pred4s/preprod/preVRE";
// DEFAULT_TEST_SCOPE_NAME = "/gcube/devsec/devVRE";
ROOT = "/gcube";
VO = ROOT + "/devsec";
VRE = VO + "/devVRE";
// VO = ROOT + "/devNext";
// VRE = VO + "/NextNext";
}
private enum Type{
USER, CLIENT_ID
};
public static void set(Secret secret) throws Exception {
SecretManagerProvider.instance.reset();
SecretManager secretManager = new SecretManager();
SecretManagerProvider.instance.set(secretManager);
SecretManager secretManager = new SecretManager();
secretManager.addSecret(secret);
secretManager.set();
SecretManagerProvider.instance.set(secretManager);
SecretManagerProvider.instance.get().set();
}
public static void setContextByName(String fullContextName) throws Exception {
logger.debug("Going to set credentials for context {}", fullContextName);
Secret secret = getSecretByContextName(fullContextName);
set(secret);
}
private static TokenResponse getJWTAccessToken(String context) throws Exception {
Type type = Type.valueOf(properties.get(TYPE_PROPERTY_KEY).toString());
TokenResponse tr = null;
int index = context.indexOf('/', 1);
String root = context.substring(0, index == -1 ? context.length() : index);
switch (type) {
case CLIENT_ID:
String clientId = properties.getProperty(CLIENT_ID_PROPERTY_KEY);
String clientSecret = properties.getProperty(root);
tr = KeycloakClientFactory.newInstance().queryUMAToken(context, clientId, clientSecret, context, null);
break;
case USER:
default:
String username = properties.getProperty(USERNAME_PROPERTY_KEY);
String password = properties.getProperty(PASSWORD_PROPERTY_KEY);
switch (root) {
case "/gcube":
default:
clientId = "next.d4science.org";
break;
case "/pred4s":
clientId = "pre.d4science.org";
break;
case "/d4science.research-infrastructures.eu":
clientId = "services.d4science.org";
break;
}
clientSecret = null;
tr = KeycloakClientHelper.getTokenForUser(context, username, password);
break;
}
return tr;
}
public static Secret getSecretByContextName(String context) throws Exception {
TokenResponse tr = getJWTAccessToken(context);
Secret secret = new JWTSecret(tr.getAccessToken());
return secret;
}
public static void setContext(String token) throws Exception {
Secret secret = getSecret(token);
set(secret);
}
public static void setContextByName(String fullContextName) throws Exception {
Secret secret = getSecretByContextName(fullContextName);
set(secret);
}
private static Secret getSecret(String token) throws Exception {
Secret secret = SecretUtility.getSecretByTokenString(token);
return secret;
}
private static Secret getSecretByContextName(String fullContextName) throws Exception {
String token = ContextTest.properties.getProperty(fullContextName);
return getSecret(token);
public static String getUser() {
String user = "UNKNOWN";
try {
user = SecretManagerProvider.instance.get().getUser().getUsername();
} catch(Exception e) {
logger.error("Unable to retrieve user. {} will be used", user);
}
return user;
}
@BeforeClass
public static void beforeClass() throws Exception {
setContextByName(VRE);
setContextByName(DEFAULT_TEST_SCOPE);
}
@AfterClass

View File

@ -0,0 +1,89 @@
/**
*
*/
package org.gcube.gcat;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.common.authorization.utils.secret.SecretUtility;
import org.junit.AfterClass;
import org.junit.BeforeClass;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ContextTestOldAuth {
protected static Properties properties;
protected static final String PROPERTIES_FILENAME = "token.properties";
public static final String ROOT;
public static final String VO;
public static final String VRE;
static {
properties = new Properties();
InputStream input = ContextTestOldAuth.class.getClassLoader().getResourceAsStream(PROPERTIES_FILENAME);
try {
// load the properties file
properties.load(input);
} catch(IOException e) {
throw new RuntimeException(e);
}
// DEFAULT_TEST_SCOPE_NAME = "/pred4s/preprod/preVRE";
// DEFAULT_TEST_SCOPE_NAME = "/gcube/devsec/devVRE";
ROOT = "/gcube";
VO = ROOT + "/devsec";
VRE = VO + "/devVRE";
// VO = ROOT + "/devNext";
// VRE = VO + "/NextNext";
}
public static void set(Secret secret) throws Exception {
SecretManagerProvider.instance.reset();
SecretManager secretManager = new SecretManager();
SecretManagerProvider.instance.set(secretManager);
secretManager.addSecret(secret);
secretManager.set();
}
public static void setContext(String token) throws Exception {
Secret secret = getSecret(token);
set(secret);
}
public static void setContextByName(String fullContextName) throws Exception {
Secret secret = getSecretByContextName(fullContextName);
set(secret);
}
private static Secret getSecret(String token) throws Exception {
Secret secret = SecretUtility.getSecretByTokenString(token);
return secret;
}
private static Secret getSecretByContextName(String fullContextName) throws Exception {
String token = ContextTestOldAuth.properties.getProperty(fullContextName);
return getSecret(token);
}
@BeforeClass
public static void beforeClass() throws Exception {
setContextByName(VRE);
}
@AfterClass
public static void afterClass() throws Exception {
SecretManagerProvider.instance.reset();
}
}

View File

@ -1,147 +0,0 @@
package org.gcube.gcat.configuration;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Set;
import java.util.stream.Collectors;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.gcat.ContextTest;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class GCoreISConfigurationProxyTest extends ContextTest {
private static final Logger logger = LoggerFactory.getLogger(GCoreISConfigurationProxyTest.class);
// @Test
public void testGetSupportedOrganizationsFromIS() throws Exception {
ContextTest.setContextByName("/gcube/devNext/NextNext");
String context = SecretManagerProvider.instance.get().getContext();
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context);
@SuppressWarnings("deprecation")
Set<String> organizations = gCoreISConfigurationProxy.getSupportedOrganizationsFromGenericResource();
Assert.assertTrue(organizations.size()>0);
}
@Test
public void testCatalogueConfiguration() throws Exception {
ContextTest.setContextByName("/gcube/devsec/devVRE");
String context = SecretManagerProvider.instance.get().getContext();
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context);
ServiceCatalogueConfiguration catalogueConfiguration = gCoreISConfigurationProxy.getCatalogueConfigurationFromIS();
String json = catalogueConfiguration.toJsonString();
logger.info("Configuration in context {} is {}", context, json);
/*
ServiceCatalogueConfiguration secondCatalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json);
String secondJson = secondCatalogueConfiguration.toJsonString();
logger.info("After marshalling and unmarshalling configuration in context {} is {}", context, secondJson);
String decryptedJson = secondCatalogueConfiguration.toJsonString(true);
logger.info("Decrypted configuration in context {} is {}", context, decryptedJson);
ServiceCatalogueConfiguration thirdCatalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(decryptedJson);
String thirdJson = thirdCatalogueConfiguration.toJsonString();
logger.info("After marshalling and unmarshalling decrypted configuration in context {} is {}", context, thirdJson);
logger.info("All as JsonArray [{},{},{},{}]", json, secondJson, decryptedJson, thirdJson);
*/
}
// @Test
public void updateConfigurationToNewVersion() throws Exception {
ContextTest.setContextByName("/gcube/devsec/devVRE");
String context = SecretManagerProvider.instance.get().getContext();
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context);
@SuppressWarnings("deprecation")
ServiceCatalogueConfiguration catalogueConfiguration = gCoreISConfigurationProxy.getOLDCatalogueConfigurationFromIS();
String json = catalogueConfiguration.toJsonString();
logger.debug("Read configuration {}", json);
catalogueConfiguration = gCoreISConfigurationProxy.createOrUpdateOnIS();
json = catalogueConfiguration.toJsonString();
logger.debug("Updated configuration {}", json);
}
public static String DEVVRE_CONFIG_JSON = "devvre.conf.json";
//@Test
public void createConfiguration() throws Exception {
ContextTest.setContextByName("/gcube/devsec/devVRE");
String context = SecretManagerProvider.instance.get().getContext();
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(DEVVRE_CONFIG_JSON);
String json = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json);
GCoreISConfigurationProxy gCoreISConfigurationProxy = new GCoreISConfigurationProxy(context, catalogueConfiguration);
gCoreISConfigurationProxy.createOnIS();
}
// protected GenericResource instantiateGenericResource(String secondaryType, String name, String xml) throws Exception {
// GenericResource genericResource = new GenericResource();
// org.gcube.common.resources.gcore.GenericResource.Profile profile = genericResource.newProfile();
// profile.type(secondaryType);
// profile.name(name);
// profile.description("This resource is read by gCat and define the list of CKAN organizations where a client is allowed to publish for the current context");
// 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 xml) throws Exception {
// GenericResource genericResource = instantiateGenericResource(
// GCoreISConfigurationProxy.GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS,
// GCoreISConfigurationProxy.GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS, 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 String createGRBody(List<String> organizations) throws Exception {
// if(organizations==null || organizations.size()<1) {
// throw new Exception("Unable to create the body for the generic resource with empty organization list");
// }
// ObjectMapper objectMapper = new ObjectMapper();
// ObjectNode objectNode = objectMapper.createObjectNode();
// ArrayNode arrayNode = objectNode.putArray(GCoreISConfigurationProxy.GENERIC_RESOURCE_CKAN_ORGANIZATIONS);
// for(String organizationName : organizations) {
// arrayNode.add(organizationName);
// }
// return objectMapper.writeValueAsString(objectNode);
// }
//
// protected void createGenericResourceForSupportedOrganizations(List<String> organizations) throws Exception {
// String json = createGRBody(organizations);
// createGenericResource(json);
// }
//
// // @Test
// public void createGenericResourceForSupportedOrganizationsByName() throws Exception {
// List<String> organizations = new ArrayList<>();
// organizations.add("nextnext");
// organizations.add("devvre");
// createGenericResourceForSupportedOrganizations(organizations);
// }
//
// // @Test
// public void createGenericResourceForSupportedOrganizationsByScopeBean() throws Exception {
// ContextTest.setContextByName("/gcube/devNext/NextNext");
//
// List<ScopeBean> scopeBeans = new ArrayList<>();
// scopeBeans.add(new ScopeBean("/gcube/devNext/NextNext"));
// scopeBeans.add(new ScopeBean("/gcube/devsec/devVRE"));
// List<String> organizations = new ArrayList<>();
// for(ScopeBean scopeBean : scopeBeans) {
// organizations.add(CatalogueConfiguration.getOrganizationName(scopeBean));
// }
// createGenericResourceForSupportedOrganizations(organizations);
// }
}

View File

@ -1,23 +0,0 @@
package org.gcube.gcat.configuration;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class VersionTest {
private static final Logger logger = LoggerFactory.getLogger(VersionTest.class);
@Test
public void testVersions() {
String[] versions = new String[]{"2.2.0-SNAPSHOT", "1.3.5", "1.4.5-beta", "1.5.6-gcore"};
for(String v : versions) {
Version version = new Version(v);
logger.debug("Version is {}", version);
}
}
}

View File

@ -1,58 +0,0 @@
package org.gcube.gcat.moderation;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.api.moderation.CMItemStatus;
import org.gcube.gcat.moderation.thread.ModerationThread;
import org.gcube.gcat.persistence.ckan.CKANUser;
import org.junit.Test;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ModerationThreadTest extends ContextTest {
protected ModerationThread getModerationThread(CKANUser ckanUser) {
ModerationThread moderationThread = ModerationThread.getDefaultInstance();
moderationThread.setItemCoordinates("b1040e70-774f-47b6-95e9-f24efca50caf", "my_first_restful_transaction_model", "RESTful Transaction Model", "https://data.dev.d4science.org/ctlg/devVRE/my_first_restful_transaction_model");
moderationThread.setCKANUser(ckanUser);
return moderationThread;
}
@Test
// @Ignore
public void testModerationThread() throws Exception {
ContextTest.setContextByName("pasquale.pagano_/gcube/devsec/devVRE");
CKANUser ckanUser = new CKANUser();
ckanUser.setName(CKANUser.getCKANUsername());
ckanUser.read();
ModerationThread moderationThread = getModerationThread(ckanUser);
moderationThread.postItemCreated();
moderationThread = getModerationThread(ckanUser);
moderationThread.postItemUpdated();
moderationThread = getModerationThread(ckanUser);
moderationThread.postUserMessage(CMItemStatus.PENDING, "Pensaci Bene");
moderationThread = getModerationThread(ckanUser);
moderationThread.postItemRejected(null);
moderationThread = getModerationThread(ckanUser);
moderationThread.postItemRejected("reject con messaggio: Non mi garba");
moderationThread = getModerationThread(ckanUser);
moderationThread.postItemApproved(null);
moderationThread = getModerationThread(ckanUser);
moderationThread.postItemApproved("approve con messaggio: Ora mi garba");
moderationThread = getModerationThread(ckanUser);
moderationThread.setItemAuthor(true);
moderationThread.postUserMessage(CMItemStatus.APPROVED, "Grazie");
Thread.sleep(1000);
}
}

View File

@ -1,14 +0,0 @@
package org.gcube.gcat.oldutils;
import org.gcube.gcat.ContextTest;
import org.junit.Test;
public class ValidatorTest extends ContextTest {
@Test
public void createGroupAsSysAdmin() throws Exception {
String groupName = "Italian";
Validator validator = new Validator();
validator.createGroupAsSysAdmin(groupName);
}
}

View File

@ -1,87 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import java.util.Arrays;
import java.util.List;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.gcat.ContextTest;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANGroupTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANGroupTest.class);
@Ignore
@Test
public void count() throws Exception {
CKANGroup ckanGroup = new CKANGroup();
int count = ckanGroup.count();
logger.debug("The groups are {}", count);
}
@Ignore
@Test
public void list() throws Exception {
CKANGroup ckanGroup = new CKANGroup();
String ret = ckanGroup.list(10000, 0);
logger.debug("{}", ret);
}
@Ignore
@Test
public void read() throws Exception {
CKANGroup ckanGroup = new CKANGroup();
ckanGroup.setApiKey(CKANUtility.getSysAdminAPI());
String name = "abundance-level";
ckanGroup.setName(name);
String ret = ckanGroup.read();
logger.debug("{}", ret);
}
@Ignore
@Test
public void createDeleteGroup() throws Exception {
CKANGroup ckanGroup = new CKANGroup();
ckanGroup.setApiKey(CKANUtility.getSysAdminAPI());
String name = "my-test-group";
ckanGroup.setName(name);
ckanGroup.create();
ckanGroup.delete(true);
}
public static List<String> listGroup() throws Exception {
CKANGroup group = new CKANGroup();
String groups = group.list(1000, 0);
ObjectMapper objectMapper = new ObjectMapper();
String[] groupArray = objectMapper.readValue(groups, String[].class);
return Arrays.asList(groupArray);
}
// @Test
public void listAllGroups() throws Exception {
ContextTest.setContextByName("");
CKANGroup ckanGroup = new CKANGroup();
ckanGroup.setApiKey(CKANUtility.getSysAdminAPI());
List<String> groups = listGroup();
logger.debug("Groups are:\n{}", groups);
}
// @Test
public void deleteAllGroups() throws Exception {
ContextTest.setContextByName("/gcube");
CKANGroup ckanGroup = new CKANGroup();
ckanGroup.setApiKey(CKANUtility.getSysAdminAPI());
List<String> groups = listGroup();
for(String name : groups) {
ckanGroup.setName(name);
// ckanGroup.delete(true);
}
}
}

View File

@ -1,22 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CKANInstanceTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANInstanceTest.class);
@Test
public void testSerialization() throws Exception {
CatalogueConfiguration configuration = CatalogueConfigurationFactory.getInstance();
ObjectMapper mapper = new ObjectMapper();
logger.debug("Configuration is {}", mapper.writeValueAsString(configuration));
}
}

View File

@ -1,49 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.gcube.gcat.ContextTest;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
public class CKANLicenseTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANLicenseTest.class);
@Test
public void list() throws Exception {
CKANLicense license = new CKANLicense();
license.list(-1, -1);
JsonNode gotList = license.getJsonNodeResult();
Assert.assertTrue(gotList instanceof ArrayNode);
ObjectMapper mapper = new ObjectMapper();
logger.debug("List :\n{}", mapper.writeValueAsString(gotList));
}
@Test
public void testCheckLicense() throws Exception {
ArrayNode arrayNode = CKANLicense.getLicenses();
for(JsonNode jsonNode : arrayNode) {
String licenseId = jsonNode.get(CKAN.ID_KEY).asText();
assertTrue(CKANLicense.checkLicenseId(arrayNode, licenseId));
logger.debug("'{}' is a valid License ID", licenseId);
}
List<String> invalidIds = new ArrayList<>();
invalidIds.add("InvaliLicense");
invalidIds.add("CCO");
for(String licenseId : invalidIds) {
assertFalse(CKANLicense.checkLicenseId(arrayNode, licenseId));
logger.debug("As expected '{}' is an INVALID License ID", licenseId);
}
}
}

View File

@ -1,104 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import java.util.Arrays;
import java.util.List;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CKANOrganizationTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANOrganizationTest.class);
/*
@Test
public void getUserRole() throws Exception {
CKANOrganization ckanOrganization = new CKANOrganization();
ckanOrganization.setApiKey(CKANUtility.getSysAdminAPI());
ckanOrganization.setName(CKANOrganization.getCKANOrganizationName());
String ret = ckanOrganization.getUserRole("luca.frosini");
logger.debug("{}", ret);
}
*/
@Test
public void countOrganizations() throws Exception {
CKANOrganization ckanOrganization = new CKANOrganization();
int count = ckanOrganization.count();
logger.debug("The organizations are {}", count);
}
public static List<String> listOrg() throws Exception {
CKANOrganization ckanOrganization = new CKANOrganization();
String ret = ckanOrganization.list(1000, 0);
ObjectMapper objectMapper = new ObjectMapper();
String[] groupArray = objectMapper.readValue(ret, String[].class);
return Arrays.asList(groupArray);
}
@Test
public void listOrganizations() throws Exception {
CKANOrganization ckanOrganization = new CKANOrganization();
String ret = ckanOrganization.list(1000, 0);
logger.debug("{}", ret);
}
@Ignore
@Test
public void deleteOrganization() throws Exception {
ContextTest.setContextByName("/gcube");
CKANOrganization ckanOrganization = new CKANOrganization();
ckanOrganization.setApiKey(CKANUtility.getSysAdminAPI());
String name = "aquamaps1";
ckanOrganization.setName(name);
logger.debug("Going to delete {}", name);
// ckanOrganization.delete(true);
}
@Ignore
@Test
public void createOrganization() throws Exception {
ContextTest.setContextByName("/gcube/devNext/NextNext");
String context = SecretManagerProvider.instance.get().getContext();
ScopeBean scopeBean = new ScopeBean(context);
CKANOrganization ckanOrganization = new CKANOrganization();
ckanOrganization.setApiKey(CKANUtility.getSysAdminAPI());
String name = CatalogueConfiguration.getOrganizationName(scopeBean);
ckanOrganization.setName(name);
String json = "{\"display_name\": \"" + scopeBean.name() + "\",\"description\": \"" + context + " Organization\",\"name\": \"" + name + "\"}";
logger.info("Going to create Organization {} : {}", name, json);
// ckanOrganization.create(json);
}
//@Ignore
@Test
public void deleteAllOrganizations() throws Exception {
ContextTest.setContextByName("/gcube");
CKANOrganization ckanOrganization = new CKANOrganization();
ckanOrganization.setApiKey(CKANUtility.getSysAdminAPI());
String ret = ckanOrganization.list(1000, 0);
logger.debug("{}", ret);
ObjectMapper mapper = new ObjectMapper();
JsonNode organizations = (ArrayNode) mapper.readTree(ret);
for(JsonNode jn : organizations) {
ckanOrganization.setName(jn.asText());
logger.debug("Going to delete organization '{}'", jn.asText());
try {
// ckanOrganization.delete(true);
}catch (Exception e) {
// A not empty organization cannot be deleted
}
}
}
}

View File

@ -1,602 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
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.common.authorization.library.provider.UserInfo;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.api.GCatConstants;
import org.gcube.gcat.api.configuration.CatalogueConfiguration;
import org.gcube.gcat.api.moderation.CMItemStatus;
import org.gcube.gcat.api.moderation.Moderated;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANPackageTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANPackageTest.class);
private static final String NOTES_KEY = "notes";
private static final String URL_KEY = "url";
private static final String PRIVATE_KEY = "private";
public static final String ITEM_NAME_VALUE = "restful_transaction_model";
private static final String LICENSE_VALUE = "CC-BY-SA-4.0";
private static final String EXTRAS_TYPE_VALUE_VALUE = "EmptyProfile";
@Test
public void count() throws Exception {
CKANPackage ckanPackage = new CKANPackage();
int count = ckanPackage.count();
logger.debug("Number of items in {} is {}", SecretManagerProvider.instance.get().getContext(), count);
}
@Test
public void list() throws Exception {
ContextTest.setContextByName("/gcube/devsec/devVRE");
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
// mvm.add(Moderated.CM_ITEM_STATUS_QUERY_PARAMETER, CMItemStatus.PENDING.getValue());
UriInfo uriInfo = getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
ObjectMapper mapper = new ObjectMapper();
String ret = ckanPackage.list(10, 0);
JsonNode gotList = mapper.readTree(ret);
Assert.assertTrue(gotList instanceof ArrayNode);
logger.debug("List:\n{}", mapper.writeValueAsString(gotList));
}
public static UriInfo getUriInfo(MultivaluedMap<String, String> queryParameters) {
UriInfo uriInfo = new UriInfo() {
@Override
public URI resolve(URI uri) {
return null;
}
@Override
public URI relativize(URI uri) {
return null;
}
@Override
public UriBuilder getRequestUriBuilder() {
return null;
}
@Override
public URI getRequestUri() {
return null;
}
@Override
public MultivaluedMap<String, String> getQueryParameters(boolean decode) {
return null;
}
@Override
public MultivaluedMap<String, String> getQueryParameters() {
return queryParameters;
}
@Override
public List<PathSegment> getPathSegments(boolean decode) {
return null;
}
@Override
public List<PathSegment> getPathSegments() {
return null;
}
@Override
public MultivaluedMap<String, String> getPathParameters(boolean decode) {
return null;
}
@Override
public MultivaluedMap<String, String> getPathParameters() {
return null;
}
@Override
public String getPath(boolean decode) {
return null;
}
@Override
public String getPath() {
return null;
}
@Override
public List<String> getMatchedURIs(boolean decode) {
return null;
}
@Override
public List<String> getMatchedURIs() {
return null;
}
@Override
public List<Object> getMatchedResources() {
return null;
}
@Override
public UriBuilder getBaseUriBuilder() {
return null;
}
@Override
public URI getBaseUri() {
return null;
}
@Override
public UriBuilder getAbsolutePathBuilder() {
return null;
}
@Override
public URI getAbsolutePath() {
return null;
}
};
return uriInfo;
}
@Test
public void listWithParameters() throws Exception {
String contextName = "/gcube/devNext/NextNext";
ScopeBean scopeBean = new ScopeBean(contextName);
String ckanOrganizationName = CatalogueConfiguration.getOrganizationName(scopeBean);
ContextTest.setContextByName(contextName);
CKANPackage ckanPackage = new CKANPackage();
ObjectMapper mapper = new ObjectMapper();
boolean[] values = new boolean[]{true, false};
for(boolean includeValidOrganization : values) {
for(boolean includeFakeOrganization : values) {
MultivaluedMap<String,String> queryParameters = new MultivaluedHashMap<>();
StringWriter stringWriter = new StringWriter();
boolean addOr = false;
if(includeFakeOrganization) {
stringWriter.append("organization:fakeorganization");
addOr = true;
}
if(includeValidOrganization) {
if(addOr) {
stringWriter.append(" OR ");
}
stringWriter.append("organization:");
stringWriter.append(ckanOrganizationName);
}
String filter = stringWriter.toString();
if(filter.length()>0) {
queryParameters.add(GCatConstants.Q_KEY, filter);
}
queryParameters.add(GCatConstants.OWN_ONLY_QUERY_PARAMETER, Boolean.TRUE.toString());
/*
queryParameters.add("fl","[\"name\"]");
*/
/*
queryParameters.add("facet.field","[\"name\"]");
queryParameters.add("sort","name asc");
*/
Map<String,String> parameters = null;
try {
ckanPackage.setUriInfo(getUriInfo(queryParameters));
parameters = ckanPackage.getListingParameters(10, 0);
}catch (ForbiddenException e) {
if(includeFakeOrganization) {
// This is the expected behaviour
continue;
}else {
throw e;
}
}
String ret = ckanPackage.list(parameters);
JsonNode gotList = mapper.readTree(ret);
Assert.assertTrue(gotList instanceof ArrayNode);
logger.debug("List :\n{}", mapper.writeValueAsString(gotList));
}
}
}
@Test(expected=ForbiddenException.class)
public void checkParameter() throws Exception {
ContextTest.setContextByName("/gcube/devNext/NextNext");
Map<String, String> parameters = new HashMap<>();
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String,String> queryParameters = new MultivaluedHashMap<>();
queryParameters.add(GCatConstants.Q_KEY, "organization:nextnext OR organization:fakeorg");
parameters = ckanPackage.checkListParameters(queryParameters, parameters);
logger.debug("{}", parameters);
}
/*
* PRE
*
* Workspace(luca.frosini) > RESTful Transaction Model.pdf
* https://data1-d.d4science.org/shub/E_YjI4STdKKzRlNjgzMm9jQWxjcmtReDNwbDFYR3lpTHo3SjdtN1RDZ3c2OGk0ZHZhdE5iZElBKzNxUDAyTGFqZw==
* https://goo.gl/HcUWni
*
*
* Workspace(luca.frosini) > RESTful Transaction Model v 1.0.pdf
* https://data1-d.d4science.org/shub/E_aThRa1NpWFJpTGEydEU2bEJhMXNjZy8wK3BxekJKYnpYTy81cUkwZVdicEZ0aGFRZmY4MkRnUC8xWW0zYzVoVg==
* https://goo.gl/J8AwQW
* ContextTest.setContextByName("/gcube/devsec/devVRE");
*
* Workspace(luca.frosini) > RESTful Transaction Model v 1.1.pdf
* https://data1-d.d4science.org/shub/E_NkhrbVV4VTluT0RKVUtCRldobFZTQU5ySTZneFdpUzJ2UjJBNlZWNDlURDVHamo4WjY5RnlrcHZGTGNkT2prUg==
* https://goo.gl/78ViuR
*
*/
@Test
public void testNameRegex() {
Map<String,Boolean> stringsToTest = new HashMap<>();
stringsToTest.put("Test", false); // Fails for T
stringsToTest.put("test-test+test-test", false); // Fails for +
stringsToTest.put("t", false); // Fails because is too short. Min length is 2 characters
stringsToTest.put("te", true);
stringsToTest.put("test-test_test-test", true);
stringsToTest.put(
"test-test_test-test_test-test_test-test_test-test_test-test_test-test_test-test_test-test_test-test_",
true);
// // Fails because is too long. Max length is 100 characters
stringsToTest.put(
"test-test_test-test_test-test_test-test_test-test_test-test_test-test_test-test_test-test_test-test_t",
false);
for(String testString : stringsToTest.keySet()) {
boolean match = testString.matches(CKANPackage.NAME_REGEX);
logger.debug("'{}' does {}match the regex {}", testString, match ? "" : "NOT ", CKANPackage.NAME_REGEX);
Assert.assertEquals(stringsToTest.get(testString), match);
}
}
protected CKANPackage createPackage(ObjectMapper mapper, Boolean socialPost) throws Exception {
String currentContext = SecretManagerProvider.instance.get().getContext();
ScopeBean scopeBean = new ScopeBean(currentContext);
String organization = CatalogueConfiguration.getOrganizationName(scopeBean);
ObjectNode itemObjectNode = mapper.createObjectNode();
itemObjectNode.put(CKAN.NAME_KEY, ITEM_NAME_VALUE);
itemObjectNode.put(CKANPackage.TITLE_KEY, "RESTful Transaction Model");
itemObjectNode.put(CKANPackage.LICENSE_KEY, LICENSE_VALUE);
itemObjectNode.put(PRIVATE_KEY, false);
itemObjectNode.put(NOTES_KEY, "A research of Luca Frosini");
itemObjectNode.put(URL_KEY, "https://www.d4science.org");
itemObjectNode.put(CKANPackage.OWNER_ORG_KEY, organization);
ArrayNode tagArrayNode = itemObjectNode.putArray(CKANPackage.TAGS_KEY);
ObjectNode tagNode = mapper.createObjectNode();
tagNode.put(CKANPackage.NAME_KEY, "REST");
tagArrayNode.add(tagNode);
ArrayNode resourceArrayNode = itemObjectNode.putArray(CKANPackage.RESOURCES_KEY);
ObjectNode resourceNode = mapper.createObjectNode();
resourceNode.put(CKANResource.NAME_KEY, "RESTful Transaction Model");
// Workspace(luca.frosini) > RESTful Transaction Model v 1.0.pdf
resourceNode.put(CKANResource.URL_KEY, "https://goo.gl/J8AwQW");
resourceArrayNode.add(resourceNode);
ArrayNode extraArrayNode = itemObjectNode.putArray(CKANPackage.EXTRAS_KEY);
ObjectNode typeNode = mapper.createObjectNode();
typeNode.put(CKANPackage.EXTRAS_KEY_KEY, CKANPackage.EXTRAS_KEY_VALUE_SYSTEM_TYPE);
typeNode.put(CKANPackage.EXTRAS_VALUE_KEY, EXTRAS_TYPE_VALUE_VALUE);
extraArrayNode.add(typeNode);
/*
ObjectNode modelNode = mapper.createObjectNode();
modelNode.put(CKANPackage.EXTRAS_KEY_KEY, "test");
modelNode.put(CKANPackage.EXTRAS_VALUE_KEY, "test 2.9°");
extraArrayNode.add(modelNode);
*/
ObjectNode populationNode = mapper.createObjectNode();
populationNode.put(CKANPackage.EXTRAS_KEY_KEY, "Population");
populationNode.put(CKANPackage.EXTRAS_VALUE_KEY, "Italian");
extraArrayNode.add(populationNode);
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
//ckanPackage.setApiKey(CKANUtility.getSysAdminAPI());
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
mvm.add(GCatConstants.SOCIAL_POST_QUERY_PARAMETER, socialPost.toString());
UriInfo uriInfo = getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
String createdItem = ckanPackage.create(mapper.writeValueAsString(itemObjectNode));
logger.debug(createdItem);
return ckanPackage;
}
@Test
public void create() throws Exception {
ObjectMapper mapper = new ObjectMapper();
createPackage(mapper, true);
}
@Test
public void testURIResolver() {
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName("Test");
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = objectMapper.createObjectNode();
ckanPackage.addItemURLViaResolver(objectNode);
}
@Test
public void createReadUpdateUpdatePurge() throws Exception {
ObjectMapper mapper = new ObjectMapper();
createPackage(mapper, false);
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
String readItem = ckanPackage.read();
JsonNode readItemObjectNode = mapper.readTree(readItem);
String readID = readItemObjectNode.get(CKANPackage.NAME_KEY).asText();
Assert.assertNotNull(readID);
Assert.assertTrue(ITEM_NAME_VALUE.compareTo(readID) == 0);
String updatedNotes = "A research of Luca Frosini made during the PhD";
((ObjectNode) readItemObjectNode).put(NOTES_KEY, updatedNotes);
ArrayNode resources = (ArrayNode) readItemObjectNode.get(CKANPackage.RESOURCES_KEY);
ObjectNode objectNode = (ObjectNode) resources.get(0);
// Workspace(luca.frosini) > RESTful Transaction Model v 1.1.pdf
objectNode.put(CKANResource.URL_KEY, "https://goo.gl/78ViuR");
resources.set(0, objectNode);
((ObjectNode) readItemObjectNode).replace(CKANPackage.RESOURCES_KEY, resources);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
String updatedItem = ckanPackage.update(mapper.writeValueAsString(readItemObjectNode));
logger.trace(updatedItem);
JsonNode updatedItemObjectNode = mapper.readTree(updatedItem);
String gotUpdatedNotes = updatedItemObjectNode.get(NOTES_KEY).asText();
Assert.assertTrue(gotUpdatedNotes.compareTo(updatedNotes) == 0);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
((ObjectNode) updatedItemObjectNode).remove(CKANPackage.RESOURCES_KEY);
String secondUpdateItem = ckanPackage.update(mapper.writeValueAsString(updatedItemObjectNode));
logger.trace(secondUpdateItem);
/*
ObjectNode patchObjectNode = mapper.createObjectNode();
String patchedNotes = updatedNotes + " in October 2018";
patchObjectNode.put(NOTES_KEY, patchedNotes);
patchObjectNode.put(CKANPackage.NAME_KEY, ITEM_NAME_VALUE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
String patchedItem = ckanPackage.patch(mapper.writeValueAsString(patchObjectNode));
logger.trace(patchedItem);
JsonNode patchedItemObjectNode = mapper.readTree(patchedItem);
String gotPatchedNotes = patchedItemObjectNode.get(NOTES_KEY).asText();
Assert.assertTrue(gotPatchedNotes.compareTo(patchedNotes)==0);
*/
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.purge();
logger.debug("Item {} purge successfully", ITEM_NAME_VALUE);
}
@Test
//(expected = NotFoundException.class)
public void read() throws Exception {
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
String ret = ckanPackage.read();
logger.debug(ret);
}
@Ignore
@Test
//(expected = NotFoundException.class)
public void delete() throws Exception {
delete(true);
}
public void delete(boolean purge) throws Exception {
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.delete(purge);
}
@Test
public void testUpdate() {
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName("knime_workflow_with_joined_consumer_phase_and_dose_response_model");
ckanPackage.update("{\"rating\":0.0,\"license_title\":\"Academic Free License 3.0\",\"maintainer\":\"\",\"relationships_as_object\":[],\"private\":false,\"maintainer_email\":\"\",\"num_tags\":5,\"id\":\"f4292d0e-c94f-4542-bfa3-25f78638fc1b\",\"metadata_created\":\"2020-01-07T16:40:16.987780\",\"owner_org\":\"3571cca5-b0ae-4dc6-b791-434a8e062ce5\",\"metadata_modified\":\"2020-02-03T14:24:59.221160\",\"author\":\"Buschhardt Tasja\",\"author_email\":\"tasja.buschhardt@bfr.bund.de\",\"state\":\"active\",\"version\":\"1\",\"license_id\":\"AFL-3.0\",\"type\":\"dataset\",\"resources\":[{\"cache_last_updated\":null,\"cache_url\":null,\"mimetype_inner\":null,\"hash\":\"\",\"description\":\"\",\"format\":\"knwf\",\"url\":\"https://data.d4science.net/g9QY\",\"created\":\"2019-09-16T20:49:02.168666\",\"state\":\"active\",\"package_id\":\"f4292d0e-c94f-4542-bfa3-25f78638fc1b\",\"last_modified\":null,\"mimetype\":null,\"url_type\":null,\"position\":0,\"revision_id\":\"a84a35ec-2786-4835-9f50-ad52202c4e33\",\"size\":null,\"datastore_active\":false,\"id\":\"0734b380-ea5d-4c99-be03-c38ff6ae6fbf\",\"resource_type\":null,\"name\":\"KNIME_WF_ICPMF11\"}],\"num_resources\":1,\"tags\":[{\"vocabulary_id\":null,\"state\":\"active\",\"display_name\":\"Campylobacter\",\"id\":\"84c76669-d135-4c5e-9e3a-b163689a10de\",\"name\":\"Campylobacter\"},{\"vocabulary_id\":null,\"state\":\"active\",\"display_name\":\"KNIME workflow\",\"id\":\"30bce4d2-fc45-46ab-8f8b-5da582fff3c3\",\"name\":\"KNIME workflow\"},{\"vocabulary_id\":null,\"state\":\"active\",\"display_name\":\"chicken meat\",\"id\":\"f1aac698-a865-4bf4-ac55-b53f8bf7ecac\",\"name\":\"chicken meat\"},{\"vocabulary_id\":null,\"state\":\"active\",\"display_name\":\"consumer phase model\",\"id\":\"9e6b2337-9ac0-4bfc-8e7f-4327c531bbec\",\"name\":\"consumer phase model\"},{\"vocabulary_id\":null,\"state\":\"active\",\"display_name\":\"dose response model\",\"id\":\"21311c09-a928-4a9a-83de-7ef98b257af5\",\"name\":\"dose response model\"}],\"groups\":[],\"creator_user_id\":\"7020f836-45f4-4ee8-9c65-e7504209644f\",\"relationships_as_subject\":[],\"name\":\"knime_workflow_with_joined_consumer_phase_and_dose_response_model\",\"isopen\":true,\"url\":\"\",\"notes\":\"This KNIME workflow shows how to use FSK-Lab to read, customise, run, \\r\\ncombine and change simulation settings for food safety models- specifically \\r\\nthis workflow exemplifies a joined consumer phase model for Campylobacter \\r\\nin chicken meat and a dose response model for Campylobacter\",\"title\":\"KNIME workflow with joined consumer phase and dose response model\",\"extras\":[{\"value\":\"\",\"key\":\"Author\"},{\"value\":\"https://data.d4science.org/ctlg/RAKIP_trial/knime_workflow_with_joined_consumer_phase_and_dose_response_model\",\"key\":\"Item URL\"},{\"value\":\"ResearchObject\",\"key\":\"system:type\"},{\"value\":\"{\\\"relatedIdentifierType\\\":\\\"URL\\\",\\\"relationType\\\":\\\"isReferencedBy\\\",\\\"link\\\":\\\"https://doi.org/10.5072/zenodo.488235\\\",\\\"zenodoId\\\":488235}\",\"key\":\"relatedIdentifier:Zenodo.DOI\"}],\"license_url\":\"https://www.opensource.org/licenses/AFL-3.0\",\"ratings_count\":0,\"organization\":{\"description\":\"\",\"title\":\"devVRE\",\"created\":\"2016-05-30T11:30:41.710079\",\"approval_status\":\"approved\",\"is_organization\":true,\"state\":\"active\",\"image_url\":\"\",\"revision_id\":\"7c8463df-ed3f-4d33-87d8-6c0bcbe30d5d\",\"type\":\"organization\",\"id\":\"3571cca5-b0ae-4dc6-b791-434a8e062ce5\",\"name\":\"devvre\"},\"revision_id\":\"9f51fa28-0732-46c9-a208-9a0e6da0cd2c\"}");
}
@Test
public void testUpdate2() {
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName("vre_picture");
ckanPackage.update("{\"rating\": 0.0, \"license_title\": \"Academic Free License 3.0\", \"maintainer\": \"Kerekes Kata\", \"relationships_as_object\": [], \"private\": false, \"maintainer_email\": \"kerekeska@nebih.gov.hu\", \"num_tags\": 1, \"id\": \"7731b70f-47ff-4b74-b943-188215e82d07\", \"metadata_created\": \"2020-01-07T16:40:24.822719\", \"owner_org\": \"3571cca5-b0ae-4dc6-b791-434a8e062ce5\", \"metadata_modified\": \"2020-02-03T15:16:42.596068\", \"author\": \"Kerekes Kata\", \"author_email\": \"kerekeska@nebih.gov.hu\", \"state\": \"active\", \"version\": \"1\", \"license_id\": \"AFL-3.0\", \"type\": \"dataset\", \"resources\": [{\"cache_last_updated\": null, \"cache_url\": null, \"mimetype_inner\": null, \"hash\": \"\", \"description\": \"\", \"format\": \"JPEG\", \"url\": \"https://goo.gl/SnwAM7\", \"created\": \"2019-04-01T13:24:40.738838\", \"state\": \"active\", \"package_id\": \"7731b70f-47ff-4b74-b943-188215e82d07\", \"last_modified\": null, \"mimetype\": \"image/jpeg\", \"url_type\": null, \"position\": 0, \"revision_id\": \"06d61000-a0c1-4155-ad2d-78ede56d6bb5\", \"size\": null, \"datastore_active\": false, \"id\": \"1de8851d-1385-47ae-9c93-6040d170a9cc\", \"resource_type\": null, \"name\": \"th.jpeg\"}], \"num_resources\": 1, \"tags\": [{\"vocabulary_id\": null, \"state\": \"active\", \"display_name\": \"DEMETER\", \"id\": \"4e05058b-a006-4dbf-94f5-277a30318323\", \"name\": \"DEMETER\"}], \"groups\": [], \"creator_user_id\": \"7020f836-45f4-4ee8-9c65-e7504209644f\", \"relationships_as_subject\": [], \"name\": \"vre_picture\", \"isopen\": true, \"url\": \"\", \"notes\": \"This is a nice picture of a VRE ;)\", \"title\": \"VRE picture\", \"extras\": [{\"value\": \"https://data.d4science.org/ctlg/DEMETER_trial/vre_picture\", \"key\": \"Item URL\"}, {\"value\": \"ResearchObject\", \"key\": \"system:type\"}], \"license_url\": \"https://www.opensource.org/licenses/AFL-3.0\", \"ratings_count\": 0, \"organization\": {\"description\": \"\", \"title\": \"devVRE\", \"created\": \"2016-05-30T11:30:41.710079\", \"approval_status\": \"approved\", \"is_organization\": true, \"state\": \"active\", \"image_url\": \"\", \"revision_id\": \"7c8463df-ed3f-4d33-87d8-6c0bcbe30d5d\", \"type\": \"organization\", \"id\": \"3571cca5-b0ae-4dc6-b791-434a8e062ce5\", \"name\": \"devvre\"}, \"revision_id\": \"bdb6169a-6268-43d6-b7e1-265c0c9e1a1c\"}");
}
@Test
public void generateLinoTokenModeration() throws Exception {
UserInfo userInfo = new UserInfo("leonardo.candela", new ArrayList<>());
String userToken = authorizationService().generateUserToken(userInfo, VRE);
logger.debug(userToken);
}
@Test
public void testModeration() throws Exception {
CKANPackage ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
try {
ckanPackage.purge();
}catch (NotFoundException e) {
logger.trace("The item does not exist. This is correct");
}
ObjectMapper mapper = new ObjectMapper();
createPackage(mapper, false);
ContextTest.setContextByName("leonardo.candela_"+VRE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.message("Please add the notes.");
ContextTest.setContextByName(VRE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
String readItem = ckanPackage.read();
JsonNode readItemObjectNode = mapper.readTree(readItem);
String updatedNotes = "A research of Luca Frosini made during the PhD";
((ObjectNode) readItemObjectNode).put(NOTES_KEY, updatedNotes);
String ret = ckanPackage.update(mapper.writeValueAsString(readItemObjectNode));
logger.debug("Updated {}", ret);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.message("I hope now it can be approved.");
ContextTest.setContextByName("leonardo.candela_"+VRE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.reject("You must specify the institution.");
ContextTest.setContextByName(VRE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
readItem = ckanPackage.read();
readItemObjectNode = mapper.readTree(readItem);
updatedNotes = "A research of Luca Frosini at ISTI";
((ObjectNode) readItemObjectNode).put(NOTES_KEY, updatedNotes);
ckanPackage.update(mapper.writeValueAsString(readItemObjectNode));
ContextTest.setContextByName("pasquale.pagano_"+VRE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.approve("It seems fine now");
ContextTest.setContextByName(VRE);
ckanPackage = new CKANPackage();
ckanPackage.setName(ITEM_NAME_VALUE);
ckanPackage.purge();
}
@Ignore
// @Test
public void deleteAllInASpecificOrganization() {
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
mvm.add(GCatConstants.Q_KEY, "organization:devvre");
mvm.add(GCatConstants.OWN_ONLY_QUERY_PARAMETER, "false");
UriInfo uriInfo = getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
// String res = ckanPackage.deleteAll(true);
// logger.debug("{}", res);
}
@Ignore
// @Test
public void deleteAllItemsInAllOrganizations() {
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
mvm.add(GCatConstants.OWN_ONLY_QUERY_PARAMETER, "false");
UriInfo uriInfo = getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
// String res = ckanPackage.deleteAll(true);
// logger.debug("{}", res);
}
@Test
public void listTest() {
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
mvm.add(GCatConstants.OWN_ONLY_QUERY_PARAMETER, "False");
UriInfo uriInfo = getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
String res = ckanPackage.list(10, 0);
logger.debug("{}", res);
}
@Test
public void teet() {
Boolean b = Boolean.parseBoolean("false");
logger.debug("{}", b);
b = Boolean.parseBoolean("False");
logger.debug("{}", b);
}
}

View File

@ -1,139 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.gcat.ContextTest;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CKANResourceTest extends ContextTest {
private static final Logger logger = LoggerFactory.getLogger(CKANResourceTest.class);
/*
* PRE
*
* Workspace(luca.frosini) > RESTful Transaction Model.pdf
* https://data1-d.d4science.org/shub/E_YjI4STdKKzRlNjgzMm9jQWxjcmtReDNwbDFYR3lpTHo3SjdtN1RDZ3c2OGk0ZHZhdE5iZElBKzNxUDAyTGFqZw==
* https://goo.gl/HcUWni
*
*
* Workspace(luca.frosini) > RESTful Transaction Model v 1.0.pdf
* https://data1-d.d4science.org/shub/E_aThRa1NpWFJpTGEydEU2bEJhMXNjZy8wK3BxekJKYnpYTy81cUkwZVdicEZ0aGFRZmY4MkRnUC8xWW0zYzVoVg==
* https://goo.gl/J8AwQW
*
*
* Workspace(luca.frosini) > RESTful Transaction Model v 1.1.pdf
* https://data1-d.d4science.org/shub/E_NkhrbVV4VTluT0RKVUtCRldobFZTQU5ySTZneFdpUzJ2UjJBNlZWNDlURDVHamo4WjY5RnlrcHZGTGNkT2prUg==
* https://goo.gl/78ViuR
*
*/
@Ignore
@Test
public void testCopyStorageResource() throws Exception {
// ContextTest.setContextByName("/d4science.research-infrastructures.eu/D4Research/AGINFRAplusDev");
URL url = new URL("https://data.d4science.net/gS9k");
String itemID = UUID.randomUUID().toString();
CKANResource ckanResource = new CKANResource(itemID);
ckanResource.resourceID = UUID.randomUUID().toString();
URL finalURL = ckanResource.copyStorageResource(url);
logger.debug("Initial URL is {} - Final URL is {}", url, finalURL);
ckanResource.deleteStorageResource(finalURL, ckanResource.resourceID, ckanResource.mimeType);
}
@Ignore
@Test
public void testCreate() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = objectMapper.createObjectNode();
objectNode.put(CKANResource.NAME_KEY, "MyTestTy_rest_upload");
objectNode.put(CKANResource.URL_KEY, "https://data.d4science.org/shub/58a13287-3e91-4afd-bd80-cf4605a0edaa");
objectNode.put("description", "i uploaded this file using the REST API");
// objectNode.put(CKANResource.ID_KEY, "ba7ab7e8-c268-4219-98cd-c73470870999");
CKANResource ckanResource = new CKANResource("ba7ab7e8-c268-4219-98cd-c73470870999");
String json = ckanResource.getAsString(objectNode);
logger.debug("Going to create Resource {}", json);
ckanResource.create(objectNode);
}
@Test
public void testCreateUpdate() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = objectMapper.createObjectNode();
objectNode.put(CKANResource.NAME_KEY, "MyTestTy_rest_upload");
objectNode.put(CKANResource.URL_KEY, "https://data-dev.d4science.net/C1G2");
objectNode.put("description", "File uploaded using the REST API");
// objectNode.put(CKANResource.ID_KEY, "ba7ab7e8-c268-4219-98cd-c73470870999");
String id = "5795a7ce-4444-439e-b77d-1df3beaa8a42"; // name = my_restful
CKANResource ckanResource = new CKANResource(id);
String json = ckanResource.getAsString(objectNode);
logger.debug("Going to create Resource {}", json);
String createdJson = ckanResource.create(objectNode);
ckanResource = new CKANResource(id);
ckanResource.update(createdJson);
}
// @Test
public void testDelete() throws Exception {
CKANResource ckanResource = new CKANResource("f0326fec-d8ac-42c7-abff-c7905b4d938e");
ckanResource.setResourceID("fcf98272-41e7-4f05-9294-fdafb1a33074");
ckanResource.delete();
}
@Test
public void testGetFormat() {
CKANResource ckanResource = new CKANResource(UUID.randomUUID().toString());
ckanResource.mimeType = "application/zip";
ckanResource.originalFileExtension = ".fskk";
String format = ckanResource.getFormat();
Assert.assertTrue(format.compareTo("fskk") == 0);
ckanResource.originalFileExtension = null;
format = ckanResource.getFormat();
Assert.assertTrue(format.compareTo("zip") == 0);
ckanResource.mimeType = "image/jpeg";
format = ckanResource.getFormat();
Assert.assertTrue(format.compareTo("jpg") == 0);
}
@Test
public void testFinalURIResolverURL() throws Exception {
List<String> urlsString = new ArrayList<>();
urlsString.add("https://data-pre.d4science.net/wgsZ");
urlsString.add("https://data-dev.d4science.net/TzQv");
urlsString.add("https://data-d.d4science.org/shub/E_MkxMbitjYzY3R3VlTmp1cDF6cHVHY2w1Zk15RTdFakUrZnpKYXVLN1R6T0dtT1FXaXpIOHJmb2dSZ2p4WS9hYw==");
urlsString.add("https://data.d4science.net/Cuvn");
//urlsString.add("");
for(String urlString : urlsString) {
try {
URL url = CKANResource.getFinalURL(new URL(urlString));
System.out.println(urlString + " -> " + url.toString());
logger.debug("{} -> {}", urlString, url.toString());
}catch (Exception e) {
System.out.println("Error while getting final URL for " + urlString);
logger.debug("Error while getting final URL for {}", urlString);
}
}
}
}

View File

@ -1,94 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import org.gcube.gcat.ContextTest;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
public class CKANUserTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANUserTest.class);
private static final String USERNAME = "lucio_lelii";
private CKANUser getCKANUser() {
CKANUser user = new CKANUser();
user.setApiKey(CKANUtility.getSysAdminAPI());
user.setName(USERNAME);
return user;
}
@Test
public void getUsernameFromCkanUsername() {
String username = "luca.frosini";
String ckanUsername = CKANUser.getCKANUsername(username);
Assert.assertTrue(ckanUsername.compareTo("luca_frosini")==0);
String gotUsername = CKANUser.getUsernameFromCKANUsername(ckanUsername);
Assert.assertTrue(gotUsername.compareTo(username)==0);
}
@Test
public void list() throws Exception {
CKANUser ckanUser = getCKANUser();
String ret = ckanUser.list(-1, -1);
logger.debug("{}", ret);
}
@Test
public void create() throws Exception {
CKANUser ckanUser = getCKANUser();
String ret = ckanUser.createInCkan();
logger.debug("{}", ret);
}
@Test
public void read() throws Exception {
CKANUser ckanUser = getCKANUser();
String ret = ckanUser.read();
logger.debug("{}", ret);
}
public final static String DISPLAY_NAME = "display_name";
@Test
public void update() throws Exception {
CKANUser ckanUser = getCKANUser();
String ret = ckanUser.read();
ObjectMapper mapper = new ObjectMapper();
JsonNode readUser = mapper.readTree(ret);
((ObjectNode) readUser).put(CKANUser.EMAIL, USERNAME + "@gcube.ckan.org");
((ObjectNode) readUser).put("state", "active");
ret = ckanUser.update(ckanUser.getAsString(readUser));
logger.debug("{}", ret);
}
@Test
public void delete() throws Exception {
CKANUser ckanUser = getCKANUser();
ckanUser.delete(false);
}
@Test
public void testAddSpecialUserToOrganization() throws Exception {
CKANUser ckanUser = getCKANUser();
ckanUser.addUserToOrganization();
}
@Test
public void checkAndUpdateInformation() throws Exception {
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
logger.debug("{}", ckanUser.result);
ckanUser = CKANUserCache.getCurrrentCKANUser();
logger.debug("{}", ckanUser.result);
ckanUser = CKANUserCache.getCurrrentCKANUser();
logger.debug("{}", ckanUser.result);
}
}

View File

@ -1,21 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import org.gcube.gcat.ContextTest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class CKANUtilityTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKANPackageTest.class);
@Test
public void testGetApiKey() throws Exception {
String ckanAPI = CKANUtility.getApiKey();
logger.debug("User {} has the following API key {}", CKANUser.getCKANUsername(), ckanAPI);
}
}

View File

@ -1,178 +0,0 @@
package org.gcube.gcat.persistence.ckan;
import javax.ws.rs.NotFoundException;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.gcat.ContextTest;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI-CNR)
*/
public class CKanPackageTrashTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(CKanPackageTrashTest.class);
protected boolean find(ArrayNode ids, String name) {
boolean found = false;
for(int i=0; i<ids.size(); i++) {
if(name.compareTo(ids.get(i).asText())==0) {
found = true;
break;
}
}
return found;
}
@Test
public void testListAndEmptyTrash() throws Exception {
// Cleanign the env
ContextTest.setContextByName(VRE);
CKANPackageTest ckanPackageTest = new CKANPackageTest();
ckanPackageTest.delete(true);
ckanPackageTest = new CKANPackageTest();
ckanPackageTest.create();
ckanPackageTest.delete(false);
CKANPackageTrash ckanPackageTrash = new CKANPackageTrash();
ArrayNode ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
boolean found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(found);
ckanPackageTest.delete(true);
ids = ckanPackageTrash.getItems();
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
logger.debug("{}", ids);
}
public static final String NON_CATALOGUE_ADMIN_USER = "lucio.lelii";
@Test
public void testListAndEmptyTrashFromAdmin() throws Exception {
// Cleanign the env
ContextTest.setContextByName(VRE);
CKANPackageTest ckanPackageTest = new CKANPackageTest();
try {
ckanPackageTest.delete(true);
}catch (NotFoundException e) {
// It is expected. the env was clean
}
ContextTest.setContextByName(NON_CATALOGUE_ADMIN_USER+"_"+VRE);
ckanPackageTest = new CKANPackageTest();
ckanPackageTest.create();
ckanPackageTest.delete(false);
// I'm admin
ContextTest.setContextByName(VRE);
CKANPackageTrash ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(false);
ArrayNode ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
boolean found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(found);
ckanPackageTrash.setOwnOnly(true);
ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
ContextTest.setContextByName(NON_CATALOGUE_ADMIN_USER+"_"+VRE);
ckanPackageTest.delete(true);
// I'm admin
ContextTest.setContextByName(VRE);
ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(false);
ids = ckanPackageTrash.getItems();
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
logger.debug("{}", ids);
}
@Test
public void testListAndEmptyTrashFromNonAdmin() throws Exception {
// Cleaning the env
ContextTest.setContextByName(VRE);
CKANPackageTest ckanPackageTest = new CKANPackageTest();
try {
ckanPackageTest.delete(true);
}catch (NotFoundException e) {
// It is expected. the env was clean
}
ContextTest.setContextByName(VRE);
ckanPackageTest = new CKANPackageTest();
ckanPackageTest.create();
ckanPackageTest.delete(false);
// He is not admin
ContextTest.setContextByName(NON_CATALOGUE_ADMIN_USER+"_"+VRE);
CKANPackageTrash ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(false);
ArrayNode ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
boolean found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
ckanPackageTrash.setOwnOnly(true);
ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
ContextTest.setContextByName(VRE);
ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(false);
ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(found);
ckanPackageTrash.setOwnOnly(true);
ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(found);
ckanPackageTest.delete(true);
ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(false);
ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
ckanPackageTrash.setOwnOnly(true);
ids = ckanPackageTrash.getItems();
logger.debug("{}", ids);
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
ContextTest.setContextByName(NON_CATALOGUE_ADMIN_USER+"_"+VRE);
ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(false);
ids = ckanPackageTrash.getItems();
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
logger.debug("{}", ids);
ckanPackageTrash = new CKANPackageTrash();
ckanPackageTrash.setOwnOnly(true);
ids = ckanPackageTrash.getItems();
found = find(ids, CKANPackageTest.ITEM_NAME_VALUE);
Assert.assertTrue(!found);
logger.debug("{}", ids);
}
}

View File

@ -1,424 +0,0 @@
package org.gcube.gcat.persistence.grsf;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
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.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.api.GCatConstants;
import org.gcube.gcat.persistence.ckan.CKANGroup;
import org.gcube.gcat.persistence.ckan.CKANGroupTest;
import org.gcube.gcat.persistence.ckan.CKANOrganization;
import org.gcube.gcat.persistence.ckan.CKANOrganizationTest;
import org.gcube.gcat.persistence.ckan.CKANPackage;
import org.gcube.gcat.persistence.ckan.CKANPackageTest;
import org.gcube.gcat.persistence.ckan.CKANUser;
import org.gcube.gcat.persistence.ckan.CKANUtility;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class GRSFUtilities extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(GRSFUtilities.class);
public static final String PRODUCTION_GRSF = "/d4science.research-infrastructures.eu/FARM/GRSF";
public static final String PRODUCTION_GRSF_ADMIN = "/d4science.research-infrastructures.eu/FARM/GRSF_Admin";
public static final String PRODUCTION_GRSF_PRE = "/d4science.research-infrastructures.eu/FARM/GRSF_Pre";
public static final String PREPROD_GRSF_PRE = "/pred4s/preprod/GRSF_Pre";
@Before
public void before() throws Exception {
// ContextTest.setContextByName(PREPROD_GRSF_PRE);
// ContextTest.setContextByName(PRODUCTION_GRSF);
// ContextTest.setContextByName(PRODUCTION_GRSF_ADMIN);
// ContextTest.setContextByName(PRODUCTION_GRSF_PRE);
// logger.debug(SecretManagerProvider.instance.get().getUser().getUsername());
// logger.debug(SecretManagerProvider.instance.get().getContext());
}
private void create(Set<String> createdGroup, Map<String, String> groups, String name) throws JsonProcessingException, IOException {
if(createdGroup.contains(name)) {
return;
}
String sysAdminAPI = ""; // CKANUtility.getSysAdminAPI();
CKANGroup ckanGroupToCreate = new CKANGroup();
ckanGroupToCreate.setApiKey(sysAdminAPI);
ckanGroupToCreate.setName(name);
// ckanGroupToCreate.purge();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(groups.get(name));
ArrayNode array = (ArrayNode) jsonNode.get("groups");
for(JsonNode node : array) {
String parentName = node.get("name").asText();
if(!createdGroup.contains(parentName)) {
create(createdGroup, groups, parentName);
}
}
ckanGroupToCreate.create(groups.get(name));
createdGroup.add(name);
}
// @Test
public void createGRSFGroups() throws ObjectNotFound, Exception {
String key = CKANUtility.getSysAdminAPI();
List<String> groupNames = CKANGroupTest.listGroup();
Map<String, String> groups = new HashMap<>();
for(String name : groupNames) {
CKANGroup ckanGroup = new CKANGroup();
ckanGroup.setApiKey(key);
ckanGroup.setName(name);
String read = ckanGroup.read();
groups.put(name, read);
}
Set<String> createdGroup = new HashSet<>();
for(String name : groupNames) {
create(createdGroup, groups, name);
}
}
public static String getGroupNameOnCkan(String origName){
if(origName == null) {
throw new IllegalArgumentException("origName cannot be null");
}
String modified = origName.replaceAll("\\(", "");
modified = modified.replaceAll("\\)", "");
modified = modified.trim().toLowerCase().replaceAll("[^A-Za-z0-9-]", "-");
if(modified.startsWith("-")) {
modified = modified.substring(1);
}
if(modified.endsWith("-")) {
modified = modified.substring(0, modified.length() -1);
}
return modified;
}
public static final String GROUP_SUFFIX = "-group";
public static String getGroupId(String name) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(getGroupNameOnCkan(name));
if(!name.endsWith(GROUP_SUFFIX)) {
stringBuffer.append(GROUP_SUFFIX);
}
return stringBuffer.toString();
}
@Test
public void testGroupName() {
String[] groupNames = new String[] {
"GRSF", "Legacy", "Fishery", "Stock", "FIRMS", "FishSource", "RAM",
"Catch", "Landing", "Assessment Method",
"Abundance Level", "Abundance Level (FIRMS Standard)", "Biomass",
"Fishing Pressure", "Fishing Pressure (FIRMS Standard)", "State and Trend",
"FAO Stock Status Category", "Scientific Advice",
"GRSF SDG Flag", "GRSF Traceability Flag"
};
for(String name : groupNames) {
String ckanGroupName = getGroupId(name);
String groupName = CKANGroup.fromGroupTitleToName(name) + GROUP_SUFFIX;
logger.info("{} -> {}", name, ckanGroupName, groupName);
Assert.assertTrue(ckanGroupName.compareTo(groupName)==0);
}
}
// @Test
public void createNewGRSFGroups() throws ObjectNotFound, Exception {
String sysAdminAPI = CKANUtility.getSysAdminAPI();
String[] groupNames = new String[] {
"GRSF", "Legacy", "Fishery", "Stock", "FIRMS", "FishSource", "RAM",
"Catch", "Landing", "Assessment Method",
"Abundance Level", "Abundance Level (FIRMS Standard)", "Biomass",
"Fishing Pressure", "Fishing Pressure (FIRMS Standard)", "State and Trend",
"FAO Stock Status Category", "Scientific Advice",
"GRSF SDG Flag", "GRSF Traceability Flag"
};
groupNames = new String[] {"Assessment Method"};
ObjectMapper objectMapper = new ObjectMapper();
for(String name : groupNames) {
CKANGroup ckanGroupToCreate = new CKANGroup();
ckanGroupToCreate.setApiKey(sysAdminAPI);
ckanGroupToCreate.setName(name);
ObjectNode node = objectMapper.createObjectNode();
node.put("display_name", name);
node.put("title", name);
node.put("name", getGroupId(name));
String json = objectMapper.writeValueAsString(node);
logger.info(json);
ckanGroupToCreate.create(json);
}
}
// @Test
public void deleteNewGRSFGroups() throws ObjectNotFound, Exception {
String sysAdminAPI = CKANUtility.getSysAdminAPI();
// String[] groupNames = new String[] {
// "GRSF", "Legacy", "Fishery", "Stock", "FIRMS", "FishSource", "RAM",
// "Catch", "Landing",
// "Abundance Level", "Abundance Level (FIRMS Standard)", "Biomass",
// "Fishing Pressure", "Fishing Pressure (FIRMS Standard)", "State and Trend",
// "FAO Stock Status Category", "Scientific Advice",
// "GRSF SDG Flag", "GRSF Traceability Flag"
// };
//
List<String> groupNames = CKANGroupTest.listGroup();
logger.debug(SecretManagerProvider.instance.get().getUser().getUsername());
logger.debug(SecretManagerProvider.instance.get().getContext());
for(String name : groupNames) {
CKANGroup ckanGroupToCreate = new CKANGroup();
ckanGroupToCreate.setApiKey(sysAdminAPI);
// ckanGroupToCreate.setName(getGroupId(name));
ckanGroupToCreate.setName(name);
try {
// ckanGroupToCreate.purge();
}catch (Exception e) {
}
}
}
// @Test
public void associateUserToAllCKANGroupsAndOrganization() throws ObjectNotFound, Exception {
String[] usernames = new String[] { "grsf_publisher", "luca_frosini", "francesco_mangiacrapa"};
String sysAdminAPI = CKANUtility.getSysAdminAPI();
List<String> groupNames = CKANGroupTest.listGroup();
for(String groupName : groupNames) {
CKANUser ckanUser = new CKANUser();
ckanUser.setApiKey(sysAdminAPI);
for(String username : usernames) {
ckanUser.setName(username);
ckanUser.addToGroup(groupName);
}
}
List<String> orgs = CKANOrganizationTest.listOrg();
for(String org : orgs) {
CKANUser ckanUser = new CKANUser();
ckanUser.setApiKey(sysAdminAPI);
for(String username : usernames) {
ckanUser.addUserToOrganization(org, username, "admin");
}
}
}
// @Test
public void manageOrganizations() throws JsonProcessingException {
String context = SecretManagerProvider.instance.get().getContext();
ScopeBean scopeBean = new ScopeBean(context);
String sysAdminAPI = CKANUtility.getSysAdminAPI();
Map<String, String> organizations = new HashMap<>();
organizations.put(scopeBean.name(), scopeBean.name());
organizations.put("FIRMS", "FIRMS");
organizations.put("FishSource", "FishSource");
organizations.put("RAM", "RAM");
organizations.put("SDG", "FAO SDG 14.4.1 Questionnaire");
ObjectMapper objectMapper = new ObjectMapper();
for(String org : organizations.keySet()) {
CKANOrganization ckanOrganization = new CKANOrganization();
ckanOrganization.setApiKey(sysAdminAPI);
ckanOrganization.setName(org.toLowerCase());
ObjectNode node = objectMapper.createObjectNode();
String fancyName = organizations.get(org);
node.put("display_name", fancyName);
node.put("title", fancyName);
node.put("name", org.toLowerCase());
String json = objectMapper.writeValueAsString(node);
try {
// ckanOrganization.create(json);
// ckanOrganization.purge();
}catch (Exception e) {
}
}
}
protected void printLine(File file, String line) throws Exception {
try (FileWriter fw = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter out = new PrintWriter(bw)) {
out.println(line);
out.flush();
} catch (IOException e) {
throw e;
}
}
public Map<String,String> getListParameter(int limit, int offset, String type, String org) {
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(CKANPackage.ROWS_KEY, String.valueOf(limit));
if(offset < 0) {
offset = 0;
}
parameters.put(CKANPackage.START_KEY, String.valueOf(offset));
String q = null;
if(type!=null) {
// This filter by type
StringWriter qStringWriter = new StringWriter();
qStringWriter.append("extras_systemtype:");
qStringWriter.append(type);
q = qStringWriter.toString();
}
if(org!=null) {
StringWriter qStringWriter = new StringWriter();
qStringWriter.append("organization:");
qStringWriter.append(org);
if(q!=null) {
q = q + " AND " + qStringWriter.toString();
}
}
if(q!=null) {
q = q + " AND (StatusoftheRecord:Approved OR StatusoftheRecord:Archived)";
// q = q + " AND (groups:stock-group)";
}
if(q!=null) {
parameters.put(GCatConstants.Q_KEY, q);
}
return parameters;
}
// @Test
public void list() throws Exception {
ObjectMapper mapper = new ObjectMapper();
int limit = 100;
// String[] contexts = new String[] {PRODUCTION_GRSF, PRODUCTION_GRSF_ADMIN};
String[] contexts = new String[] {PRODUCTION_GRSF};
for(String context : contexts) {
ContextTest.setContextByName(context);
logger.debug(SecretManagerProvider.instance.get().getUser().getUsername());
logger.debug(SecretManagerProvider.instance.get().getContext());
CKANPackage ckanPackage = new CKANPackage();
SortedSet<String> sortedSet = new TreeSet<>();
String[] types = new String[] {"Marine Resource", "Assessment Unit"};
for(String type : types) {
int offset = 0;
boolean go = true;
ScopeBean scopeBean = new ScopeBean(context);
while(go) {
Map<String,String> parameters = getListParameter(limit, offset==0 ? 0 : (offset*limit), type, scopeBean.name().toLowerCase());
String ret = ckanPackage.list(parameters);
JsonNode gotList = mapper.readTree(ret);
Assert.assertTrue(gotList instanceof ArrayNode);
ArrayNode itemsArray = (ArrayNode) gotList;
if(itemsArray.size()>0) {
for(JsonNode jsonNode : itemsArray) {
String name = jsonNode.asText();
sortedSet.add(name);
}
offset++;
}else {
go=false;
}
}
File file = new File(scopeBean.name() + "-" + type.replace(" ", "_")+".txt");
if(file.exists()) {
file.delete();
}
for(String name : sortedSet) {
printLine(file, name);
}
}
}
}
private void purgeGRSFRecords(int limit, int offset) throws Exception {
logger.debug("Going to purge {} records, starting from {}", limit, limit*offset);
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
UriInfo uriInfo = CKANPackageTest.getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
ObjectMapper mapper = new ObjectMapper();
boolean go = true;
while(go) {
Map<String,String> parameters = getListParameter(limit, offset, null, null);
String ret = ckanPackage.list(parameters);
JsonNode gotList = mapper.readTree(ret);
Assert.assertTrue(gotList instanceof ArrayNode);
ArrayNode itemsArray = (ArrayNode) gotList;
if(itemsArray.size()>0) {
for(JsonNode jsonNode : itemsArray) {
String name = jsonNode.asText();
logger.debug("Going to purge record with name {}", name);
ckanPackage.setName(name);
try {
ckanPackage.purgeNoCheckNoDeleteFiles();
}catch (Exception e) {
logger.error("Unable to purge record with name {}", name, e);
}
}
Thread.sleep(500);
}else {
go=false;
}
}
}
// @Test
public void purgeAllGRSFRecords() throws Exception {
purgeGRSFRecords(100, 0);
}
@Test
public void purgeSpecificGRSFRecord() throws Exception {
CKANPackage ckanPackage = new CKANPackage();
MultivaluedMap<String, String> mvm = new MultivaluedHashMap<String,String>();
UriInfo uriInfo = CKANPackageTest.getUriInfo(mvm);
ckanPackage.setUriInfo(uriInfo);
ckanPackage.setName("70ae6895-7d3d-4f4a-86f9-bcb17d41bff6");
ckanPackage.purgeNoCheckNoDeleteFiles();
}
}

View File

@ -1,18 +1,30 @@
package org.gcube.gcat.profile;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.StringReader;
import java.net.URL;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.gcat.ContextTest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
public class ProfileTest extends ContextTest {
@ -74,15 +86,41 @@ public class ProfileTest extends ContextTest {
profile.delete("TestProfile");
}
public static String PROFILE_EXAMPLE_FILENAME = "EmptyProfileExample.xml";
public File getResourcesDirectory() throws Exception {
URL logbackFileURL = this.getClass().getClassLoader().getResource("logback-test.xml");
File logbackFile = new File(logbackFileURL.toURI());
File resourcesDirectory = logbackFile.getParentFile();
return resourcesDirectory;
}
public File getProfilesDirectory() throws Exception {
File resourcesDirectory = getResourcesDirectory();
return new File(resourcesDirectory, "profile_examples");
}
public File getSchemaDirectory() throws Exception {
File resourcesDirectory = getResourcesDirectory();
return new File(resourcesDirectory, "profile_schemas");
}
public static String PROFILE_NAME_EXAMPLE = "EmptyProfile";
public static String PROFILE_EXAMPLE_FILENAME = PROFILE_NAME_EXAMPLE + ".xml";
public String fileToString(File file) throws Exception {
try (Stream<String> lines = Files.lines(file.toPath())) {
String content = lines.collect(Collectors.joining(System.lineSeparator()));
return content;
}catch (Exception e) {
throw e;
}
}
@Test
public void testCreateUpdateDeleteGenericResource() throws Exception {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(PROFILE_EXAMPLE_FILENAME);
String xml = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
logger.debug("Body\n{}", xml);
File profilesDirectory = getProfilesDirectory();
File profileFile = new File(profilesDirectory, PROFILE_EXAMPLE_FILENAME);
String xml = fileToString(profileFile);
ISProfile profile = new ISProfile();
profile.createOrUpdate(PROFILE_NAME_EXAMPLE, xml);
/*
@ -93,4 +131,45 @@ public class ProfileTest extends ContextTest {
*/
}
protected File getSchemaFile() throws Exception {
File schemaDirectory = getSchemaDirectory();
return new File(schemaDirectory, "profile5.xsd");
}
@Test
public void validateAgainstXSD() throws Exception {
File profilesDirectory = getProfilesDirectory();
File schemaFile = getSchemaFile();
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(schemaFile.toURI().toURL());
Validator validator = schema.newValidator();
for(File profileFile : profilesDirectory.listFiles()) {
String xmlString = fileToString(profileFile);
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse( new InputSource(new StringReader(xmlString)));
DOMSource xml = new DOMSource(doc);
try {
validator.validate(xml);
logger.info("File {} has been successfuly validated against schema.", profileFile.getName());
}catch (Exception e) {
logger.error("Error while validating file {}", profileFile.getName());
throw e;
}
}
}
@Test
public void testRegex() throws Exception {
File resDirectory = getResourcesDirectory();
File regexFile = new File(resDirectory, "regex.txt");
String regex = fileToString(regexFile);
Pattern.compile(regex);
}
}

View File

@ -1,40 +0,0 @@
package org.gcube.gcat.social;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.utils.Constants;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SocialPostTest extends ContextTest {
private static final Logger logger = LoggerFactory.getLogger(SocialPostTest.class);
@Test
public void testToken() throws Exception {
Secret secret = Constants.getCatalogueSecret();
logger.debug("Application Token is {}", secret.getToken());
ContextTest.set(secret);
}
@Test
public void testSendPost() throws Exception {
SocialPost socialService = new SocialPost();
socialService.setItemID(UUID.randomUUID().toString());
socialService.setItemTitle("Test Item");
socialService.setItemURL("https://www.d4science.org");
List<String> tags = new ArrayList<>();
tags.add("Test");
tags.add("ThisIsATest");
tags.add("IgnoreIt");
socialService.setTags(tags);
socialService.sendSocialPost(false);
}
}

View File

@ -1,19 +0,0 @@
package org.gcube.gcat.social;
import org.gcube.common.authorization.utils.socialservice.SocialService;
import org.gcube.gcat.ContextTest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SocialServiceTest {
private static Logger logger = LoggerFactory.getLogger(SocialServiceTest.class);
@Test
public void get() throws Exception {
ContextTest.setContextByName("/d4science.research-infrastructures.eu/D4Research/AGINFRAplusDev");
SocialService socialService = SocialService.getSocialService();
logger.debug(socialService.getServiceBasePath());
}
}

View File

@ -1,37 +0,0 @@
package org.gcube.gcat.utils;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.gcat.ContextTest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ConstantsTest extends ContextTest {
private static final Logger logger = LoggerFactory.getLogger(ConstantsTest.class);
@Test
public void testGetApplicationToken() throws Exception {
logger.debug("Application token for Context {} is {}", SecretManagerProvider.instance.get().getContext(),
Constants.getCatalogueSecret().getToken());
}
@Test
public void decrypt() throws Exception {
StringEncrypter stringEncrypter = StringEncrypter.getEncrypter();
String decrypted = stringEncrypter.decrypt("w0KVc+78b2yUQsndDh/cXyyRquuwyILTygmoF0Y5Dls=");
logger.debug("{}", decrypted);
}
@Test
public void getToken() throws Exception {
Secret secret = Constants.getCatalogueSecret();
logger.debug(secret.getToken());
}
}

View File

@ -1,27 +0,0 @@
package org.gcube.gcat.utils;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.gcat.ContextTest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class URIResolverTest extends ContextTest {
private static final Logger logger = LoggerFactory.getLogger(URIResolverTest.class);
@Test
public void getURL() {
String catalogueItemURL = URIResolver.getCatalogueItemURL("my_first_restful_transaction_model");
logger.debug("Item URL is {}", catalogueItemURL);
}
@Test
public void decrypt() throws Exception {
ContextTest.setContextByName("/gcube/devsec/devVRE");
String encrypted = "";
String pwd = StringEncrypter.getEncrypter().decrypt(encrypted);
logger.info(pwd);
}
}

View File

@ -1,119 +0,0 @@
package org.gcube.gcat.workspace;
import java.net.URL;
import java.util.Map;
import org.gcube.common.storagehub.client.dsl.FileContainer;
import org.gcube.common.storagehub.model.Metadata;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.persistence.ckan.CKANResource;
import org.gcube.storagehub.StorageHubManagement;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CatalogueStorageHubManagementTest extends ContextTest {
private static final Logger logger = LoggerFactory.getLogger(CatalogueStorageHubManagementTest.class);
/*
* PRE
*
* Workspace(luca.frosini) > RESTful Transaction Model.pdf
* https://data1-d.d4science.org/shub/E_YjI4STdKKzRlNjgzMm9jQWxjcmtReDNwbDFYR3lpTHo3SjdtN1RDZ3c2OGk0ZHZhdE5iZElBKzNxUDAyTGFqZw==
* https://goo.gl/HcUWni
*
* Workspace(luca.frosini) > RESTful Transaction Model v 1.0.pdf
* https://data1-d.d4science.org/shub/E_aThRa1NpWFJpTGEydEU2bEJhMXNjZy8wK3BxekJKYnpYTy81cUkwZVdicEZ0aGFRZmY4MkRnUC8xWW0zYzVoVg==
* https://goo.gl/J8AwQW
*
*
* Workspace(luca.frosini) > RESTful Transaction Model v 1.1.pdf
* https://data1-d.d4science.org/shub/E_NkhrbVV4VTluT0RKVUtCRldobFZTQU5ySTZneFdpUzJ2UjJBNlZWNDlURDVHamo4WjY5RnlrcHZGTGNkT2prUg==
* https://goo.gl/78ViuR
*
*/
public static final String ORIGINAL_STORAGE_URL_STRING = "https://data1-d.d4science.org/shub/E_YjI4STdKKzRlNjgzMm9jQWxjcmtReDNwbDFYR3lpTHo3SjdtN1RDZ3c2OGk0ZHZhdE5iZElBKzNxUDAyTGFqZw==";
public static final URL ORIGINAL_STORAGE_URL;
public static final String SHORT_URL_STRING = "https://goo.gl/HcUWni";
public static final URL SHORT_STORAGE_URL;
public static final String MIME_TYPE = "application/pdf";
static {
try {
ORIGINAL_STORAGE_URL = new URL(ORIGINAL_STORAGE_URL_STRING);
SHORT_STORAGE_URL = new URL(SHORT_URL_STRING);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public static final String ITEM_ID = "MyItem";
public static final String RESOURCE_ID = "1234";
protected CatalogueStorageHubManagement catalogueStorageHubManagement;
protected StorageHubManagement storageHubManagement;
protected CatalogueMetadata catalogueMetadata;
public CatalogueStorageHubManagementTest() {
catalogueStorageHubManagement = new CatalogueStorageHubManagement();
storageHubManagement = catalogueStorageHubManagement.storageHubManagement;
catalogueMetadata = new CatalogueMetadata(ITEM_ID);
}
@Test
public void getFinalURL() {
URL finalURL = CKANResource.getFinalURL(SHORT_STORAGE_URL);
Assert.assertTrue(finalURL.toString().compareTo(ORIGINAL_STORAGE_URL_STRING) == 0);
}
protected void checkMetadata(FileContainer fileContainer, String version) {
Metadata gotMetadata = fileContainer.get().getMetadata();
Map<String,Object> gotMap = gotMetadata.getMap();
CatalogueMetadata catalogueMetadata = new CatalogueMetadata(ITEM_ID);
Metadata expectedMetadata = catalogueMetadata.getMetadata(ORIGINAL_STORAGE_URL, fileContainer.get().getName(),
RESOURCE_ID);
Map<String,Object> expectedMap = expectedMetadata.getMap();
for(String key : gotMap.keySet()) {
String value = (String) gotMap.get(key);
if(key.compareTo(CatalogueMetadata.CATALOGUE_RESOURCE_REVISION_ID) == 0) {
Assert.assertTrue(value.compareTo(version) == 0);
} else {
String expectedValue = (String) expectedMap.get(key);
Assert.assertTrue(value.compareTo(expectedValue) == 0);
}
}
}
@Test
public void testPersistence() throws Exception {
URL persistedURL = catalogueStorageHubManagement.ensureResourcePersistence(ORIGINAL_STORAGE_URL, ITEM_ID,
RESOURCE_ID);
logger.debug("Publick Link of persisted file is {}", persistedURL);
Assert.assertTrue(catalogueStorageHubManagement.getMimeType().compareTo(MIME_TYPE) == 0);
FileContainer createdFileContainer = storageHubManagement.getPersistedFile();
String version = "2";
catalogueStorageHubManagement.renameFile(RESOURCE_ID, version);
checkMetadata(createdFileContainer, version);
version = "3";
catalogueStorageHubManagement.addRevisionID(RESOURCE_ID, version);
checkMetadata(createdFileContainer, version);
catalogueStorageHubManagement.deleteResourcePersistence(ITEM_ID, RESOURCE_ID, MIME_TYPE);
}
}

View File

@ -6,3 +6,4 @@
/config.properties.old
/config.properties-new-authz
/devvre.conf.json
/config.ini

View File

@ -0,0 +1,36 @@
<metadataformat type="DataMiner Process">
<metadatafield categoryref="DataMiner Process">
<fieldName>Input Parameter</fieldName>
<mandatory>false</mandatory>
<dataType>String</dataType>
<note>Input parameter expected for the execution of the process</note>
</metadatafield>
<metadatafield categoryref="DataMiner Process">
<fieldName>Output Parameter</fieldName>
<mandatory>false</mandatory>
<dataType>String</dataType>
<note>Output parameter expected from the execution of the process </note>
</metadatafield>
<metadatafield categoryref="DataMiner Process">
<fieldName>Process Author</fieldName>
<mandatory>false</mandatory>
<dataType>String</dataType>
<maxOccurs>*</maxOccurs>
<defaultValue/>
<note>The name of the author, with email and ORCID. The format should be: family, given[, email][, ORCID]. Example: Smith, John, js@acme.org, orcid.org/0000-0002-1825-0097</note>
<validator>
<regularExpression>^[a-zA-ZÀ-ÿ .'-]+, [a-zA-ZÀ-ÿ .'-]+[,]*([a-zA-Z0-9_!#$%*+=?`{|}~^.-]+@[a-zA-Z0-9À-ÿ.-]+)?[,]*(orcid.org\/0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d\d\d-\d\d\d[\dX])?$</regularExpression>
</validator>
</metadatafield>
<metadatafield categoryref="DataMiner Process">
<fieldName>Process Maintainer</fieldName>
<mandatory>false</mandatory>
<dataType>String</dataType>
<maxOccurs>*</maxOccurs>
<defaultValue/>
<note>The name of the author, with email and ORCID. The format should be: family, given[, email][, ORCID]. Example: Smith, John, js@acme.org, orcid.org/0000-0002-1825-0097</note>
<validator>
<regularExpression>^[a-zA-ZÀ-ÿ .'-]+, [a-zA-ZÀ-ÿ .'-]+[,]*([a-zA-Z0-9_!#$%*+=?`{|}~^.-]+@[a-zA-Z0-9À-ÿ.-]+)?[,]*(orcid.org\/0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d\d\d-\d\d\d[\dX])?$</regularExpression>
</validator>
</metadatafield>
</metadataformat>

View File

@ -0,0 +1,10 @@
<metadataformat type="F2DSItem">
<metadatafield>
<fieldName>catalogue_title</fieldName>
<mandatory>true</mandatory>
<dataType>String</dataType>
<defaultValue/>
<note>The catalogue_title the item belongs to.</note>
<grouping create="true">onValue</grouping>
</metadatafield>
</metadataformat>

View File

@ -0,0 +1,36 @@
<metadataformat type="SimpleProfile" version="5">
<metadatafield>
<fieldId>test</fieldId>
<fieldName>My Test</fieldName>
<mandatory>false</mandatory>
<dataType>String</dataType>
<maxOccurs>1</maxOccurs>
<note>Test Field</note>
<tags>
<tag create="true">
<fixedValue>My Tag</fixedValue>
<ifFieldValueMatch>
<regex>^(o|O)(k|K)$</regex>
</ifFieldValueMatch>
</tag>
<tag create="true" separator="-">
<how>onFieldName_onValue</how>
<ifFieldValueMatch>
<regex>^(k|K)(o|O)$</regex>
</ifFieldValueMatch>
</tag>
</tags>
<groups>
<group create="false" propagateUp="true">
<fixedValue>My Group</fixedValue>
<ifFieldValueMatch>
<regex>^true$</regex>
</ifFieldValueMatch>
</group>
</groups>
<extra>
<srcField>myTest</srcField>
<aux>this is just a desc</aux>
</extra>
</metadatafield>
</metadataformat>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<xs:redefine schemaLocation="profile5.xsd">
<xs:complexType name="ExtraMetadataFieldType">
<xs:complexContent>
<xs:restriction base="ExtraMetadataFieldType">
<xs:sequence>
<xs:element type="GRSFType" name="grsf" minOccurs="1" maxOccurs="1" />
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="ExtraMetadataFormatType">
<xs:complexContent>
<xs:restriction base="ExtraMetadataFormatType">
<xs:sequence>
<xs:element name="grsfFields" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="GRSFType" name="grsfField" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
<xs:complexType name="GRSFType">
<xs:sequence>
<xs:element type="xs:string" name="srcField" minOccurs="1" maxOccurs="1" />
<xs:element type="SrcType" name="srcType" minOccurs="0" maxOccurs="1" />
<xs:element name="mandatory" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
<xs:enumeration value="aggregated"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element type="xs:boolean" name="isSensitive" minOccurs="1" maxOccurs="1" />
<xs:element name="dstTypes" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="DestinationType" name="dstType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="xs:string" name="topField" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element type="xs:string" name="comments" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="BaseDestinationType">
<xs:restriction base="xs:string">
<xs:enumeration value="Field"/>
<xs:enumeration value="Resource"/>
<xs:enumeration value="TimeSeries"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="DestinationType">
<xs:simpleContent>
<xs:extension base="BaseDestinationType">
<xs:attribute type="xs:string" name="maxOccur" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="FieldType">
<xs:sequence>
<xs:element type="xs:string" name="id" minOccurs="1" maxOccurs="1" />
<xs:element type="xs:string" name="type" minOccurs="1" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="SrcType">
<xs:choice>
<xs:sequence>
<xs:choice>
<xs:element name="simpleType" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="String"/>
<xs:enumeration value="Boolean"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element type="VocabularyType" name="vocabulary" minOccurs="1" maxOccurs="1" />
</xs:choice>
</xs:sequence>
<xs:sequence>
<xs:element name="complexType" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="JsonArray"/>
<xs:enumeration value="JsonObject"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="fields" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="FieldType" name="field" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:choice>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="metadataformat">
<xs:complexType>
<xs:sequence>
<xs:element name="metadatafield" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="fieldName" />
<xs:element type="xs:boolean" name="mandatory"
minOccurs="0" maxOccurs="1" />
<xs:element name="dataType">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="String" />
<xs:enumeration value="Time" />
<xs:enumeration value="Time_Interval" />
<xs:enumeration value="Times_ListOf" />
<xs:enumeration value="Text" />
<xs:enumeration value="Boolean" />
<xs:enumeration value="Number" />
<xs:enumeration value="GeoJSON" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element type="xs:string" name="maxOccurs"
minOccurs="0" maxOccurs="1" />
<xs:element type="xs:string" name="defaultValue"
minOccurs="0" maxOccurs="1" />
<xs:element type="xs:string" name="note" minOccurs="0"
maxOccurs="1" />
<xs:element name="vocabulary" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="vocabularyField"
minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute type="xs:boolean" name="isMultiSelection" />
</xs:complexType>
</xs:element>
<xs:element name="validator" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="regularExpression" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="tagging" type="TaggingType"
minOccurs="0" maxOccurs="1">
</xs:element>
<xs:element name="grouping" type="GroupingType"
minOccurs="0" maxOccurs="1">
</xs:element>
</xs:sequence>
<xs:attribute name="categoryref" use="optional" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="NotEmpty" use="required" name="type" />
<xs:attribute name="version" type="xs:integer" use="optional" fixed="3"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="TaggingGroupingValue">
<xs:restriction base="xs:string">
<xs:enumeration value="onFieldName" />
<xs:enumeration value="onValue" />
<xs:enumeration value="onFieldName_onValue" />
<xs:enumeration value="onValue_onFieldName" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TaggingType">
<xs:simpleContent>
<xs:extension base="TaggingGroupingValue">
<xs:attribute type="xs:boolean" name="create" />
<xs:attribute type="NotEmpty" name="separator" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="GroupingType">
<xs:simpleContent id="TaggingGroupingValue">
<xs:extension base="TaggingGroupingValue">
<xs:attribute type="xs:boolean" name="create" />
<xs:attribute type="xs:boolean" name="propagateUp" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="NotEmpty">
<xs:restriction base="xs:string">
<xs:minLength value="1" />
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="metadataformat">
<xs:complexType>
<xs:sequence>
<xs:element name="metadatafield" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="fieldName" />
<xs:element type="xs:boolean" name="mandatory"
minOccurs="0" maxOccurs="1" />
<xs:element name="dataType">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="String" />
<xs:enumeration value="Time" />
<xs:enumeration value="Time_Interval" />
<xs:enumeration value="Times_ListOf" />
<xs:enumeration value="Text" />
<xs:enumeration value="Boolean" />
<xs:enumeration value="Number" />
<xs:enumeration value="GeoJSON" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element type="xs:string" name="maxOccurs"
minOccurs="0" maxOccurs="1" />
<xs:element type="xs:string" name="defaultValue"
minOccurs="0" maxOccurs="1" />
<xs:element type="xs:string" name="note" minOccurs="0"
maxOccurs="1" />
<xs:choice>
<xs:element name="vocabulary" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="vocabularyField"
minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute type="xs:boolean" name="isMultiSelection" />
</xs:complexType>
</xs:element>
<xs:element name="validator" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="regularExpression" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
<xs:element name="tagging" type="TaggingType"
minOccurs="0" maxOccurs="1">
</xs:element>
<xs:element name="grouping" type="GroupingType"
minOccurs="0" maxOccurs="1">
</xs:element>
</xs:sequence>
<xs:attribute name="categoryref" use="optional" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="NotEmpty" use="required" name="type" />
<xs:attribute name="version" type="xs:integer" use="optional" fixed="4"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="TaggingGroupingValue">
<xs:restriction base="xs:string">
<xs:enumeration value="onFieldName" />
<xs:enumeration value="onValue" />
<xs:enumeration value="onFieldName_onValue" />
<xs:enumeration value="onValue_onFieldName" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TaggingType">
<xs:simpleContent>
<xs:extension base="TaggingGroupingValue">
<xs:attribute type="xs:boolean" name="create" />
<xs:attribute type="NotEmpty" name="separator" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="GroupingType">
<xs:simpleContent id="TaggingGroupingValue">
<xs:extension base="TaggingGroupingValue">
<xs:attribute type="xs:boolean" name="create" />
<xs:attribute type="xs:boolean" name="propagateUp" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="NotEmpty">
<xs:restriction base="xs:string">
<xs:minLength value="1" />
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<xs:element name="metadataformat">
<xs:complexType>
<xs:sequence>
<xs:element type="ExtendedMetadataFieldType" name="metadatafield" minOccurs="0" maxOccurs="unbounded" />
<xs:element type="ExtraMetadataFormatType" name="extra" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Any additional tag in this 'extra' tag.
This tag and its content are ignored by the validators.
They are used by any external tools producing/consuming the profile definition and instances.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
<xs:attribute type="NotEmpty" use="required" name="type">
<xs:annotation>
<xs:documentation>
The type (i.e the name) of the profile.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="version" type="xs:integer" use="optional" fixed="5">
<xs:annotation>
<xs:documentation>This XSD is valid for gCube Metadata Profile version 5. Any XML conform to this schema must declare such a version as attibute in the root tag.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:complexType name="TagGroupType">
<xs:sequence>
<xs:choice>
<xs:sequence>
<xs:element name="how" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="onFieldName"/>
<xs:enumeration value="onValue"/>
<xs:enumeration value="onFieldName_onValue"/>
<xs:enumeration value="onValue_onFieldName"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:sequence>
<xs:element type="NotEmpty" name="fixedValue" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:choice>
<xs:element name="ifFieldValueMatch" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="NotEmpty" name="regex" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="xs:boolean" name="create"/>
</xs:complexType>
<xs:complexType name="TagType">
<xs:complexContent>
<xs:extension base="TagGroupType">
<xs:attribute type="NotEmpty" name="separator" default="_"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="GroupType">
<xs:complexContent>
<xs:extension base="TagGroupType">
<xs:attribute type="xs:boolean" name="propagateUp" default="false"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="NotEmpty">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="MetadataFieldType">
<xs:sequence>
<xs:element type="xs:string" name="fieldId" minOccurs="0">
<xs:annotation>
<xs:documentation>
It declares (if present in the profile) the value that will be used to specify the field name in the Document (e.g. JSON Document) passed to Service that will store the resulting Document. If the 'fieldId' is absent in the profile, the value of the 'fieldName' (which is mandatory) will be used as field name in the Document.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element type="xs:string" name="fieldName">
<xs:annotation>
<xs:documentation>It contains the name of the metadata field.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element type="xs:boolean" name="mandatory" minOccurs="0"
maxOccurs="1">
<xs:annotation>
<xs:documentation>
It declares if the 'metadatafield' is a field mandatory (by using 'true') or not (by using 'false').
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="dataType">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="String"/>
<xs:enumeration value="Time"/>
<xs:enumeration value="Time_Interval"/>
<xs:enumeration value="Times_ListOf"/>
<xs:enumeration value="Text"/>
<xs:enumeration value="Boolean"/>
<xs:enumeration value="Number"/>
<xs:enumeration value="GeoJSON"/>
<xs:enumeration value="File"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element type="xs:string" name="maxOccurs" minOccurs="0"
maxOccurs="1">
<xs:annotation>
<xs:documentation>
It specifies the maximum number of times that the'metadatafield' can occur: N (as number): if the field must appear N times; * (as char asterisk): if the field can appear an unlimited number of times.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element type="xs:string" name="defaultValue" minOccurs="0"
maxOccurs="1"/>
<xs:element type="xs:string" name="note" minOccurs="0" maxOccurs="1"/>
<xs:choice>
<xs:element type="VocabularyType" name="vocabulary" minOccurs="0" maxOccurs="1" />
<xs:element name="validator" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="regularExpression"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
<xs:choice>
<xs:element name="tags" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="TagType" name="tag" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="tagging" type="TaggingType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
This tagging method is deprecated. Please use the tag 'tags' instead.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
<xs:choice>
<xs:element name="groups" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element type="GroupType" name="group" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="grouping" type="GroupingType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
This grouping method is deprecated. Please use the tag 'groups' instead.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
<xs:element type="xs:boolean" name="remove" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
When this field is true the field is removed from metadata.
This allows to use a field to add tag and groups depending on
any logic of this field.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
<xs:attribute name="categoryref" use="optional" type="xs:string" />
</xs:complexType>
<xs:complexType name="VocabularyType">
<xs:sequence>
<xs:element type="xs:string" name="vocabularyField"
minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute type="xs:boolean" name="isMultiSelection"/>
</xs:complexType>
<xs:complexType name="ExtraMetadataFieldType">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip">
<xs:annotation>
<xs:documentation>Any tags are allowed</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ExtraMetadataFormatType">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip">
<xs:annotation>
<xs:documentation>Any tags are allowed</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ExtendedMetadataFieldType">
<xs:complexContent>
<xs:extension base="MetadataFieldType">
<xs:sequence>
<xs:element type="ExtraMetadataFieldType" name="extra" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Any additional tag in this 'extra' tag.
This tag and its content are ignored by the validators.
They are used by any external tools producing/consuming the profile definition and instances.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- Maintain backward compatibility -->
<xs:simpleType name="TaggingGroupingValue">
<xs:restriction base="xs:string">
<xs:enumeration value="onFieldName" />
<xs:enumeration value="onValue" />
<xs:enumeration value="onFieldName_onValue" />
<xs:enumeration value="onValue_onFieldName" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TaggingType">
<xs:simpleContent>
<xs:extension base="TaggingGroupingValue">
<xs:attribute type="xs:boolean" name="create" />
<xs:attribute type="NotEmpty" name="separator" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="GroupingType">
<xs:simpleContent id="TaggingGroupingValue">
<xs:extension base="TaggingGroupingValue">
<xs:attribute type="xs:boolean" name="create" />
<xs:attribute type="xs:boolean" name="propagateUp" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1 @@
^([a-zA-ZÀ-ÿ .-]+),\s*([a-zA-ZÀ-ÿ .-]+)(?:,\s*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}))?(?:,\s*(orcid.org/\d{4}-\d{4}-\d{4}-\d{3}[0-9X]))?$