From 68c654328ad95bb5cf676496d22ffc9c16181132 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Fri, 9 Feb 2024 12:01:03 +0100 Subject: [PATCH] first commit --- .gitignore | 250 ++ .idea/.gitignore | 8 + .idea/compiler.xml | 16 + .idea/encodings.xml | 8 + .idea/jarRepositories.xml | 35 + .idea/misc.xml | 12 + .idea/vcs.xml | 6 + CHANGELOG.md | 9 + FUNDING.md | 26 + LICENSE.md | 312 ++ README.md | 48 + enunciate.xml | 23 + gcube/extra-resources/WEB-INF/gcube-app.xml | 10 + pom.xml | 243 ++ .../HelloWorldInitializator.java | 66 + .../ResourceInitializer.java | 20 + .../social_networking/rest/Attachments.java | 26 + .../social_networking/rest/Comments.java | 66 + .../social_networking/rest/HelloWorld.java | 94 + .../rest/HelloWorldExceptionMapper.java | 38 + .../gcube/social_networking/rest/Invites.java | 35 + .../gcube/social_networking/rest/Likes.java | 35 + .../social_networking/rest/Notifications.java | 40 + .../gcube/social_networking/rest/Posts.java | 113 + .../gcube/social_networking/rest/Users.java | 149 + .../gcube/social_networking/rest/Vres.java | 98 + .../server/CassandraCluster.java | 221 ++ .../server/CassandraClusterConnection.java | 501 +++ .../server/CassandraConnection.java | 49 + .../server/SocialDBDatastaxDriver.java | 3020 +++++++++++++++++ .../server/SocialDBDriver.java | 449 +++ .../gcube/social_networking/utils/Schema.java | 76 + .../META-INF/enunciate/d4science_docs.fmt | 1183 +++++++ src/main/webapp/WEB-INF/.gitignore | 5 + src/main/webapp/WEB-INF/web.xml | 19 + .../docs/css/d4science_enunciate_custom.css | 25 + 36 files changed, 7334 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 CHANGELOG.md create mode 100644 FUNDING.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 enunciate.xml create mode 100644 gcube/extra-resources/WEB-INF/gcube-app.xml create mode 100644 pom.xml create mode 100644 src/main/java/org/gcube/social_networking/HelloWorldInitializator.java create mode 100644 src/main/java/org/gcube/social_networking/ResourceInitializer.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Attachments.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Comments.java create mode 100644 src/main/java/org/gcube/social_networking/rest/HelloWorld.java create mode 100644 src/main/java/org/gcube/social_networking/rest/HelloWorldExceptionMapper.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Invites.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Likes.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Notifications.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Posts.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Users.java create mode 100644 src/main/java/org/gcube/social_networking/rest/Vres.java create mode 100644 src/main/java/org/gcube/social_networking/server/CassandraCluster.java create mode 100644 src/main/java/org/gcube/social_networking/server/CassandraClusterConnection.java create mode 100644 src/main/java/org/gcube/social_networking/server/CassandraConnection.java create mode 100644 src/main/java/org/gcube/social_networking/server/SocialDBDatastaxDriver.java create mode 100644 src/main/java/org/gcube/social_networking/server/SocialDBDriver.java create mode 100644 src/main/java/org/gcube/social_networking/utils/Schema.java create mode 100644 src/main/resources/META-INF/enunciate/d4science_docs.fmt create mode 100644 src/main/webapp/WEB-INF/.gitignore create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/docs/css/d4science_enunciate_custom.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aeccd3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,250 @@ +# Created by https://www.toptal.com/developers/gitignore/api/macos,intellij,eclipse,visualstudiocode,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,intellij,eclipse,visualstudiocode,linux + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +### Eclipse Patch ### +# Spring Boot Tooling +.sts4-cache/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/macos,intellij,eclipse,visualstudiocode,linux diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..f144907 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..2bc4296 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..cd8223c --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d31b37a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1775c93 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +# Changelog for Hello World Service + + +## [v1.0.0-SNAPSHOT] + +- First Version + diff --git a/FUNDING.md b/FUNDING.md new file mode 100644 index 0000000..9e48b94 --- /dev/null +++ b/FUNDING.md @@ -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); diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3af0507 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,312 @@ +# European Union Public Licence V. 1.1 + + +EUPL © the European Community 2007 + + +This European Union Public Licence (the “EUPL”) applies to the Work or Software +(as defined below) which is provided under the terms of this Licence. Any use of +the Work, other than as authorised under this Licence is prohibited (to the +extent such use is covered by a right of the copyright holder of the Work). + +The Original Work is provided under the terms of this Licence when the Licensor +(as defined below) has placed the following notice immediately following the +copyright notice for the Original Work: + +Licensed under the EUPL V.1.1 + +or has expressed by any other mean his willingness to license under the EUPL. + + + +## 1. Definitions + +In this Licence, the following terms have the following meaning: + +- The Licence: this Licence. + +- The Original Work or the Software: the software distributed and/or + communicated by the Licensor under this Licence, available as Source Code and + also as Executable Code as the case may be. + +- Derivative Works: the works or software that could be created by the Licensee, + based upon the Original Work or modifications thereof. This Licence does not + define the extent of modification or dependence on the Original Work required + in order to classify a work as a Derivative Work; this extent is determined by + copyright law applicable in the country mentioned in Article 15. + +- The Work: the Original Work and/or its Derivative Works. + +- The Source Code: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- The Executable Code: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. + +- The Licensor: the natural or legal person that distributes and/or communicates + the Work under the Licence. + +- Contributor(s): any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. + +- The Licensee or “You”: any natural or legal person who makes any usage of the + Software under the terms of the Licence. + +- Distribution and/or Communication: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, on-line or off-line, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + + + +## 2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a world-wide, royalty-free, non-exclusive, +sub-licensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, reproduce the Work, modify +- the Original Work, and make Derivative Works based upon the Work, communicate +- to the public, including the right to make available or display the Work or +- copies thereof to the public and perform publicly, as the case may be, the +- Work, distribute the Work or copies thereof, lend and rent the Work or copies +- thereof, sub-license rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + + + +## 3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute and/or communicate the Work. + + + +## 4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Original Work or Software, of the exhaustion of those rights or of other +applicable limitations thereto. + + + +## 5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: the Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes and/or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes and/or communicates copies of the +Original Works or Derivative Works based upon the Original Work, this +Distribution and/or Communication will be done under the terms of this Licence +or of a later version of this Licence unless the Original Work is expressly +distributed only under this version of the Licence. The Licensee (becoming +Licensor) cannot offer or impose any additional terms or conditions on the Work +or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes and/or Communicates Derivative +Works or copies thereof based upon both the Original Work and another work +licensed under a Compatible Licence, this Distribution and/or Communication can +be done under the terms of this Compatible Licence. For the sake of this clause, +“Compatible Licence” refers to the licences listed in the appendix attached to +this Licence. Should the Licensee’s obligations under the Compatible Licence +conflict with his/her obligations under this Licence, the obligations of the +Compatible Licence shall prevail. + +Provision of Source Code: When distributing and/or communicating copies of the +Work, the Licensee will provide a machine-readable copy of the Source Code or +indicate a repository where this Source will be easily and freely available for +as long as the Licensee continues to distribute and/or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + + + +## 6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + + + +## 7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +contributors. It is not a finished work and may therefore contain defects or +“bugs” inherent to this type of software development. + +For the above reason, the Work is provided under the Licence on an “as is” basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + + + +## 8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such +damage. However, the Licensor will be liable under statutory product liability +laws as far such laws apply to the Work. + + + +## 9. Additional agreements + +While distributing the Original Work or Derivative Works, You may choose to +conclude an additional agreement to offer, and charge a fee for, acceptance of +support, warranty, indemnity, or other liability obligations and/or services +consistent with this Licence. However, in accepting such obligations, You may +act only on your own behalf and on your sole responsibility, not on behalf of +the original Licensor or any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against such Contributor by the fact You have accepted +any such warranty or additional liability. + + + +## 10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon “I agree” +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution and/or Communication by You of the Work or copies thereof. + + + +## 11. Information to the public + +In case of any Distribution and/or Communication of the Work by means of +electronic communication by You (for example, by offering to download the Work +from a remote location) the distribution channel or media (for example, a +website) must at least provide to the public the information requested by the +applicable law regarding the Licensor, the Licence and the way it may be +accessible, concluded, stored and reproduced by the Licensee. + + + +## 12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + + + +## 13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work licensed hereunder. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed and/or reformed so as necessary to make +it valid and enforceable. + +The European Commission may publish other linguistic versions and/or new +versions of this Licence, so far this is required and reasonable, without +reducing the scope of the rights granted by the Licence. New versions of the +Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + + + +## 14. Jurisdiction + +Any litigation resulting from the interpretation of this License, arising +between the European Commission, as a Licensor, and any Licensee, will be +subject to the jurisdiction of the Court of Justice of the European Communities, +as laid down in article 238 of the Treaty establishing the European Community. + +Any litigation arising between Parties, other than the European Commission, and +resulting from the interpretation of this License, will be subject to the +exclusive jurisdiction of the competent court where the Licensor resides or +conducts its primary business. + + + +## 15. Applicable Law + +This Licence shall be governed by the law of the European Union country where +the Licensor resides or has his registered office. + +This licence shall be governed by the Belgian law if: + +- a litigation arises between the European Commission, as a Licensor, and any +- Licensee; the Licensor, other than the European Commission, has no residence +- or registered office inside a European Union country. + + + +## Appendix + + + +“Compatible Licences” according to article 5 EUPL are: + + +- GNU General Public License (GNU GPL) v. 2 + +- Open Software License (OSL) v. 2.1, v. 3.0 + +- Common Public License v. 1.0 + +- Eclipse Public License v. 1.0 + +- Cecill v. 2.0 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8c3e52 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Hello World Service + +This service allows any client to publish on the gCube Catalogue. + +## Built With + +* [OpenJDK](https://openjdk.java.net/) - The JDK used +* [Maven](https://maven.apache.org/) - Dependency Management + +## Documentation + +[Hello World Service](https://wiki.gcube-system.org/gcube/SmartGears) + +## Change log + +See [CHANGELOG.md](CHANGELOG.md). + +## Authors + +* **Luca Frosini** ([ORCID](https://orcid.org/0000-0003-3183-2291)) - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience) + +## How to Cite this Software + +Tell people how to cite this software. +* Cite an associated paper? +* Use a specific BibTeX entry for the software? + + @software{gcat, + author = {{Luca Frosini}}, + title = {Hello World Service}, + abstract = {This is an Hello World smargears service}, + url = {https://doi.org/10.5281/zenodo.7446641}, + keywords = {D4Science, gCube} + } + +## License + +This project is licensed under the EUPL V.1.1 License - see the [LICENSE.md](LICENSE.md) file for details. + + +## About the gCube Framework +This software is part of the [gCubeFramework](https://www.gcube-system.org/ "gCubeFramework"): an +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 see [FUNDING.md](FUNDING.md) + diff --git a/enunciate.xml b/enunciate.xml new file mode 100644 index 0000000..3524ef4 --- /dev/null +++ b/enunciate.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gcube/extra-resources/WEB-INF/gcube-app.xml b/gcube/extra-resources/WEB-INF/gcube-app.xml new file mode 100644 index 0000000..c056b4f --- /dev/null +++ b/gcube/extra-resources/WEB-INF/gcube-app.xml @@ -0,0 +1,10 @@ + + + + ${project.artifactId} + ${project.groupId} + ${project.version} + ${project.description} + /api-docs.* + /docs.* + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9d1d8d2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,243 @@ + + 4.0.0 + + org.gcube.tools + maven-parent + 1.1.0 + + org.gcube.social_networking + social-service + 1.0.0-SNAPSHOT + war + Social Service + This is the social service + + + UTF-8 + + ${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF + 2.14.0 + 4.13.0 + + + + + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + https://code-repo.d4science.org/gCubeSystem/${project.artifactId} + + + + + + org.gcube.distribution + gcube-smartgears-bom + 2.5.0 + pom + import + + + org.gcube.distribution + maven-portal-bom + 3.7.0 + pom + import + + + + + + + org.slf4j + slf4j-api + + + org.gcube.common + authorization-utils + [2.2.0, 3.0.0-SNAPSHOT) + + + org.glassfish.jersey.media + jersey-media-multipart + + + org.glassfish.jersey.containers + jersey-container-servlet + + + javax.ws.rs + javax.ws.rs-api + + + org.gcube.common.portal + portal-manager + provided + + + + org.gcube.social-networking + social-service-model + [1.2.0-SNAPSHOT, 2.0.0) + + + com.datastax.oss + java-driver-query-builder + ${cassandra.driver.oss.version} + + + com.datastax.oss + java-driver-mapper-runtime + ${cassandra.driver.oss.version} + + + com.sun.mail + javax.mail + 1.5.2 + compile + + + commons-lang + commons-lang + 2.6 + + + org.gcube.core + common-smartgears-app + + + + + javax.xml.ws + jaxws-api + provided + + + org.projectlombok + lombok + provided + + + + + org.gcube.core + common-smartgears + + + + + + com.webcohesion.enunciate + enunciate-core-annotations + ${enunciate.version} + provided + + + com.webcohesion.enunciate + enunciate-rt-util + ${enunciate.version} + provided + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + + + + junit + junit + 4.11 + test + + + ch.qos.logback + logback-classic + test + + + + + + + + kr.motd.maven + sphinx-maven-plugin + 2.10.0 + + + ${project.build.directory}/${project.artifactId}-${project.version}/docs + html + ${basedir}/docs + ${basedir}/docs + + + + process-resources + + generate + + + + + + + + com.webcohesion.enunciate + enunciate-maven-plugin + ${enunciate.version} + + + assemble + + assemble + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-enunciate-docs + process-resources + + copy-resources + + + target + + + + ${project.build.directory}/${project.artifactId}-${project.version}/api-docs + + ${project.build.directory}/api-docs + true + + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + true + + + + + + + diff --git a/src/main/java/org/gcube/social_networking/HelloWorldInitializator.java b/src/main/java/org/gcube/social_networking/HelloWorldInitializator.java new file mode 100644 index 0000000..5e0ddda --- /dev/null +++ b/src/main/java/org/gcube/social_networking/HelloWorldInitializator.java @@ -0,0 +1,66 @@ +package org.gcube.social_networking; + +import org.gcube.common.authorization.utils.manager.SecretManagerProvider; +import org.gcube.smartgears.ApplicationManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +public class HelloWorldInitializator implements ApplicationManager { + + /** + * Logger + */ + private static Logger logger = LoggerFactory.getLogger(HelloWorldInitializator.class); + + public static boolean initialised; + + /** + * {@inheritDoc} + */ + @Override + public synchronized void onInit() { + + String context = SecretManagerProvider.instance.get().getContext(); + + logger.trace( + "\n-------------------------------------------------------\n" + + "Hello World Service is Starting on context {}\n" + + "-------------------------------------------------------", + context); + +// ApplicationContext applicationContext = ContextProvider.get(); +// String helloWorldEServiceID = applicationContext.id(); + + logger.trace( + "\n-------------------------------------------------------\n" + + "Hello World Service Started Successfully on context {}\n" + + "-------------------------------------------------------", + context); + + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void onShutdown(){ + + String context = SecretManagerProvider.instance.get().getContext(); + + logger.trace( + "\n-------------------------------------------------------\n" + + "Hello World Service is Stopping on context {}\n" + + "-------------------------------------------------------", + context); + + + logger.trace( + "\n-------------------------------------------------------\n" + + "Hello World Service Stopped Successfully on context {}\n" + + "-------------------------------------------------------", + context); + } +} diff --git a/src/main/java/org/gcube/social_networking/ResourceInitializer.java b/src/main/java/org/gcube/social_networking/ResourceInitializer.java new file mode 100644 index 0000000..7bd680b --- /dev/null +++ b/src/main/java/org/gcube/social_networking/ResourceInitializer.java @@ -0,0 +1,20 @@ +package org.gcube.social_networking; + +import javax.ws.rs.ApplicationPath; + +import org.gcube.acme.rest.HelloWorld; +import org.gcube.smartgears.annotations.ManagedBy; +import org.glassfish.jersey.server.ResourceConfig; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +@ApplicationPath("/") +@ManagedBy(HelloWorldInitializator.class) +public class ResourceInitializer extends ResourceConfig { + + public ResourceInitializer() { + packages(HelloWorld.class.getPackage().toString()); + } + +} diff --git a/src/main/java/org/gcube/social_networking/rest/Attachments.java b/src/main/java/org/gcube/social_networking/rest/Attachments.java new file mode 100644 index 0000000..7c21ff3 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Attachments.java @@ -0,0 +1,26 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.Attachment; +import org.gcube.social_networking.socialnetworking.model.shared.Like; + +import javax.ws.rs.*; + +@Path("attachments") +@ResourceGroup("Attachments APIs") +@ResourceLabel("Attachments APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Attachments { + @POST + @Path("/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveAttachmentEntry(Attachment toSave) { + + } +} diff --git a/src/main/java/org/gcube/social_networking/rest/Comments.java b/src/main/java/org/gcube/social_networking/rest/Comments.java new file mode 100644 index 0000000..88d10b4 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Comments.java @@ -0,0 +1,66 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.Comment; + +import javax.ws.rs.*; +import java.util.List; + +@Path("comments") +@ResourceGroup("Comments APIs") +@ResourceLabel("Comments APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Comments { + @POST + @Path("/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void addComment(Comment comment) { + + } + + @GET + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void readComment(@PathParam("id") String id) { + + } + + @PUT + @Path("/{id}") + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void editComment(@PathParam("id") String id, Comment comment) { + + } + + @DELETE + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void deleteComment(@PathParam("id") String id) { + + } + + //the following two methods had vreid why? + @POST + @Path("/{id}/hashtags") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveHashTagsComment(@PathParam("id") String commentId, List hashtags) { + + } + + @DELETE + @Path("/{id}/hashtags") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void deleteHashTagsComment(@PathParam("id") String commentId, List hashtags) { + + } + +} diff --git a/src/main/java/org/gcube/social_networking/rest/HelloWorld.java b/src/main/java/org/gcube/social_networking/rest/HelloWorld.java new file mode 100644 index 0000000..011b04f --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/HelloWorld.java @@ -0,0 +1,94 @@ +package org.gcube.social_networking.rest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import org.gcube.social_networking.server.CassandraConnection; +import org.gcube.social_networking.socialnetworking.model.shared.Comment; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.CommentIDNotFoundException; + +@Path("comments") +@ResourceGroup("Comments APIs") +@ResourceLabel("Comments APIs") +@RequestHeaders ({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class HelloWorld { + + @POST + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void addComment(Comment comment) { + + } + + @GET + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void readComment(@PathParam("id") String id) { + + } + + @PUT + @Path("/{id}") + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void editComment(@PathParam("id") String id, Comment comment) { + + } + + @DELETE + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void deleteComment(@PathParam("id") String id) { + + } + + @POST + @Produces("application/json;charset=UTF-8") + @Consumes("application/json;charset=UTF-8") + public String create(String json) { + //Greeting g = new Greeting(); + //return g.create(json); + return "{\"text\":\"hi\"}"; + + } + + @PUT + @Path("/{greeting_name}") + @Consumes("application/json;charset=UTF-8") + @Produces("application/json;charset=UTF-8") + @StatusCodes ({ + @ResponseCode ( code = 200, condition = "The greeting has been updated successfully.") + }) +// @AuthorizationControl(allowedRoles={"boss"}, exception=NotAuthorizedException.class) + public String update(@PathParam("greeting_name") String name, String json) { + return "{}"; + } + + + @DELETE + @Path("/{greeting_name}") + @StatusCodes ({ + @ResponseCode ( code = 204, condition = "The item has been deleted successfully."), + @ResponseCode ( code = 404, condition = "The item was not found.") + }) + public String delete(@PathParam("greeting_name") String name) { + return "{}"; + } +} diff --git a/src/main/java/org/gcube/social_networking/rest/HelloWorldExceptionMapper.java b/src/main/java/org/gcube/social_networking/rest/HelloWorldExceptionMapper.java new file mode 100644 index 0000000..226468c --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/HelloWorldExceptionMapper.java @@ -0,0 +1,38 @@ +package org.gcube.social_networking.rest; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +@Provider +public class HelloWorldExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(Exception exception) { + + Status status = Status.INTERNAL_SERVER_ERROR; + String exceptionMessage = exception.getMessage(); + try { + if(exception.getCause() != null) { + exceptionMessage = exception.getCause().getMessage(); + } + } catch(Exception e) { + exceptionMessage = exception.getMessage(); + } + MediaType mediaType = MediaType.TEXT_PLAIN_TYPE; + + if(WebApplicationException.class.isAssignableFrom(exception.getClass())) { + Response gotResponse = ((WebApplicationException) exception).getResponse(); + status = Status.fromStatusCode(gotResponse.getStatusInfo().getStatusCode()); + } + + return Response.status(status).entity(exceptionMessage).type(mediaType).build(); + } + +} diff --git a/src/main/java/org/gcube/social_networking/rest/Invites.java b/src/main/java/org/gcube/social_networking/rest/Invites.java new file mode 100644 index 0000000..faaa095 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Invites.java @@ -0,0 +1,35 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.Invite; +import org.gcube.social_networking.socialnetworking.model.shared.Notification; + +import javax.ws.rs.*; + +@Path("invites") +@ResourceGroup("Invites APIs") +@ResourceLabel("Invites APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Invites { + + @POST + @Path("/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveInvite(Invite invite) { + + } + + @GET + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void readInvite(@PathParam("id") String inviteid) { + + } + +} diff --git a/src/main/java/org/gcube/social_networking/rest/Likes.java b/src/main/java/org/gcube/social_networking/rest/Likes.java new file mode 100644 index 0000000..b896e2c --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Likes.java @@ -0,0 +1,35 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.Like; +import org.gcube.social_networking.socialnetworking.model.shared.Notification; + +import javax.ws.rs.*; + +@Path("likes") +@ResourceGroup("Likes APIs") +@ResourceLabel("Likes APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Likes { + @POST + @Path("/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void like(Like like) { + + } + + //had other params why? + @DELETE + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void unlike(@PathParam("id") String likeid) { + + } + +} diff --git a/src/main/java/org/gcube/social_networking/rest/Notifications.java b/src/main/java/org/gcube/social_networking/rest/Notifications.java new file mode 100644 index 0000000..285ed04 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Notifications.java @@ -0,0 +1,40 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.Notification; +import org.gcube.social_networking.socialnetworking.model.shared.Post; + +import javax.ws.rs.*; + +@Path("notifications") +@ResourceGroup("Notifications APIs") +@ResourceLabel("Notifications APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Notifications { + @POST + @Path("/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveNotification(Notification notification) { + + } + + @PUT + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void setNotificationRead(@PathParam("id") String notid) { + + } + + @GET + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void readNotification(@PathParam("id") String notid) { + + } +} diff --git a/src/main/java/org/gcube/social_networking/rest/Posts.java b/src/main/java/org/gcube/social_networking/rest/Posts.java new file mode 100644 index 0000000..f987ef0 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Posts.java @@ -0,0 +1,113 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.Attachment; +import org.gcube.social_networking.socialnetworking.model.shared.Comment; +import org.gcube.social_networking.socialnetworking.model.shared.Post; + +import javax.ws.rs.*; +import java.util.List; + +@Path("posts") +@ResourceGroup("Posts APIs") +@ResourceLabel("Posts APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Posts { + @POST + @Path("/postuser") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveUserPost(Post post) { + + } + + @POST + @Path("/postuser") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveUserPost(Post post, List attachments){ + + } + + @PUT + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void deletePost(@PathParam("id") String postid) { + + } + + @POST + @Path("/postapp") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveAppPost(Post post) { + + } + + @POST + @Path("/postapp/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveAppPost(Post post, List attachments){ + + } + + @GET + @Path("/{id}/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void readPost(@PathParam("id") String postid){ + + } + + @GET + @Path("/portalprivacy") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllPortalPrivacyLevelPosts(){ + + } + + @GET + @Path("/{id}/comments") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllCommentByPost(@PathParam("id") String postid){ + + } + + @GET + @Path("/{id}/likes") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllLikesByPost(@PathParam("id") String postid){ + + } + + + //the following two methods had vreid why? + @POST + @Path("/{id}/hashtags") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void saveHashTags(@PathParam("id") String postid, List hashtags) { + + } + + @DELETE + @Path("/{id}/hashtags") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void deleteHashTagsComment(@PathParam("id") String postid, List hashtags) { + + } + + @GET + @Path("/{id}/attachments") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAttachmentsByPostId(@PathParam("id") String postid) { + + } + +} diff --git a/src/main/java/org/gcube/social_networking/rest/Users.java b/src/main/java/org/gcube/social_networking/rest/Users.java new file mode 100644 index 0000000..14e578c --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Users.java @@ -0,0 +1,149 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.InviteStatus; +import org.gcube.social_networking.socialnetworking.model.shared.NotificationChannelType; +import org.gcube.social_networking.socialnetworking.model.shared.NotificationType; +import org.gcube.social_networking.socialnetworking.model.shared.Post; + +import javax.ws.rs.*; +import java.util.Map; + +@Path("entities") +@ResourceGroup("Users & Apps APIs") +@ResourceLabel("Users & Apps APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Users { + + @GET + @Path("users/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllPostsByUser(@PathParam("id") String userid) { + + } + + @GET + @Path("apps/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllPostsByApp(@PathParam("id") String appid) { + + } + + @GET + @Path("users/{id}/posts/comments") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentCommentedPostsByUserAndDate(@PathParam("id") String userid, @QueryParam("time") long timeInMillis) { + + } + + @GET + @Path("users/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentPostsByUser(@PathParam("id") String userid, @QueryParam("limit") int limit) { + + } + + @GET + @Path("users/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentPostsByUser(@PathParam("id") String userid, @QueryParam("time") long timeInMillis) { + + } + + @PUT + @Path("users/{id}/notifications") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void setAllNotificationReadByUser(@PathParam("id") String userid) { + + } + + @GET + @Path("users/{id}/notifications") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getUnreadNotificationsByUser(@PathParam("id") String userid) { + + } + + @GET + @Path("users/{id}/notificationchannels") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getUserNotificationChannels(@PathParam("id") String userid, @QueryParam("type") NotificationType notificationType) { + + } + + @PUT + @Path("users/{id}/notificationpreferences") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void setUserNotificationPreferences(@PathParam("id") String userid, Map enabledChannels) { + + } + + @GET + @Path("users/{id}/notificationpreferences") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getUserNotificationPreferences(@PathParam("id") String userid) { + + } + + @GET + @Path("users/{id}/comments") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentCommentsByUserAndDate(@PathParam("id") String userid, @QueryParam("time") long timeInMillis) { + + } + + @GET + @Path("users/{id}/likes") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllLikedPostIdsByUser(@PathParam("id") String userid) { + + } + + @GET + @Path("users/{id}/likes") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllLikedPostsByUser(@PathParam("id") String userid, @QueryParam("limit") int limit) { + + } + + @GET + @Path("users/{id}/posts/likes") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentLikedPostsByUserAndDate(@PathParam("id") String userid, @QueryParam("time") long timeInMillis) { + + } + + @GET + @Path("users/{id}/notifications") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllNotificationByUser(@PathParam("id") String userid, @QueryParam("limit") int limit) { + + } + + @GET + @Path("users/{id}/notifications") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRangeNotificationsByUser(@PathParam("id") String userid, @QueryParam("limit") int limit, @QueryParam("from") int from) { + + } + + @GET + @Path("users/{id}/notifications/unread") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void checkUnreadNotifications(@PathParam("id") String userid) { + + } + + @GET + @Path("users/{id}/notifications/unread/messages") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void checkUnreadMessagesNotifications(@PathParam("id") String userid) { + + } +} diff --git a/src/main/java/org/gcube/social_networking/rest/Vres.java b/src/main/java/org/gcube/social_networking/rest/Vres.java new file mode 100644 index 0000000..53f9183 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/rest/Vres.java @@ -0,0 +1,98 @@ +package org.gcube.social_networking.rest; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import org.gcube.social_networking.socialnetworking.model.shared.InviteStatus; +import org.gcube.social_networking.socialnetworking.model.shared.Post; + +import javax.ws.rs.*; + +@Path("vres") +@ResourceGroup("VREs APIs") +@ResourceLabel("VREs APIs") +@RequestHeaders({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class Vres { + @POST + @Path("/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void savePostToVRETimeline(@PathParam("id") String vreid, Post post) { + + } + + @GET + @Path("/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllPostsByVRE(@PathParam("id") String vreid) { + + } + + @GET + @Path("/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentPostsByVRE(@PathParam("id") String vreid, @QueryParam("limit") int limit) { + + } + + @GET + @Path("/{id}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getRecentPostsByVREAndRange(@PathParam("id") String vreid, @QueryParam("limit") int limit, @QueryParam("from") int from) { + + } + + @GET + @Path("/{id}/hashtags") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getVREHashtagsWithOccurrence(@PathParam("id") String vreid) { + + } + + @GET + @Path("/{id}/hashtags") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getVREHashtagsWithOccurrenceFilteredByTime(@PathParam("id") String vreid, @QueryParam("time") long timestamp) { + + } + + @GET + @Path("/{id}/hashtags/{hashtag}/posts") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getVREPostsByHashtag(@PathParam("id") String vreid, @PathParam("hashtag") String hashtag) { + + } + + @GET + @Path("/{id}/email/{email}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void isExistingInvite(@PathParam("id") String vreid, @PathParam("email") String email) { + + } + + @PUT + @Path("/{id}/email/{email}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void setInviteStatus(@PathParam("id") String vreid, @PathParam("email") String email, InviteStatus status) { + + } + + @GET + @Path("/{id}") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + @Consumes({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getInvitedEmailsByVRE(@PathParam("id") String vreid, InviteStatus... status) { + + } + + @GET + @Path("/") + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public void getAllVREIds() { + + } +} diff --git a/src/main/java/org/gcube/social_networking/server/CassandraCluster.java b/src/main/java/org/gcube/social_networking/server/CassandraCluster.java new file mode 100644 index 0000000..f637f7f --- /dev/null +++ b/src/main/java/org/gcube/social_networking/server/CassandraCluster.java @@ -0,0 +1,221 @@ +package org.gcube.social_networking.server; + +import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Serializable; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.gcube.common.portal.GCubePortalConstants; +import org.gcube.common.portal.PortalContext; +import org.gcube.common.resources.gcore.ServiceEndpoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.TooManyRunningClustersException; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * @author Massimiliano Assante ISTI-CNR + * @author Ahmed Ibrahim ISTI-CNR + * + * @version 2.0.0 October 2023 + * + */ +@SuppressWarnings("serial") +class CassandraCluster implements Serializable { + /** + * logger + */ + private static final Logger _log = LoggerFactory.getLogger(CassandraCluster.class); + + /** + * properties to read + */ + private static final String HOST_PROPERTY = "host"; + private static final String HOST_PORT_PROPERTY = "port"; + private static final String DATACENTER_NAME_PROPERTY = "datacenter"; + private static final String KEY_SPACE_NAME_PROPERTY = "keyspace"; + /** + * other constants + */ + private final static String RUNTIME_RESOURCE_NAME = "SocialDB"; + private final static String PLATFORM_NAME = "Cassandra"; + + private static final String DEFAULT_CONFIGURATION = "/org/gcube/portal/databook/server/resources/databook.properties"; + + private static CassandraCluster singleton; + /** + * Host + */ + private String host; + /** + * Cluster Name + */ + private String datacenterName; + /** + * Keyspace Name + */ + private String keyspaceName; //to be modified + + /** + * @param infrastructureName could be null + * @return an instance of the RunningCluster + */ + public static synchronized CassandraCluster getInstance(String infrastructureName){ + if (singleton == null) { + singleton = new CassandraCluster(infrastructureName); + } + return singleton; + } + /** + * private constructor + */ + private CassandraCluster(String infrastructureName){ + //Query the IS (for the future) + try{ + List resources = getConfigurationFromIS(infrastructureName); + if (resources.size() > 1) { + _log.error("Too many Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" in this scope "); + throw new TooManyRunningClustersException("There exist more than 1 Runtime Resource in this scope having name " + + RUNTIME_RESOURCE_NAME + " and Platform " + PLATFORM_NAME + ". Only one allowed per infrasrtucture."); + } + else if (resources.size() == 0){ + _log.error("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Platform " + PLATFORM_NAME + " in this scope. Using default configuration properties: " + DEFAULT_CONFIGURATION); + loadDefaultConfiguration(); + } + else { + for (ServiceEndpoint res : resources) { + AccessPoint found = res.profile().accessPoints().iterator().next(); + host = found.address(); + datacenterName = found.description(); + keyspaceName = found.name(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + /*host = "10.1.28.55:9042, 10.1.30.142:9042, 10.1.28.100:9042"; + datacenterName = "1"; + keyspaceName = "dev_mig_consistent";*/ + } + + /** + * + * @return the + * @throws Exception + */ + private List getConfigurationFromIS(String infrastructureName) { + _log.debug("getConfigurationFromIS infrastructureName="+infrastructureName ); + String scope = "/"; + if(infrastructureName != null && !infrastructureName.isEmpty()) + scope += infrastructureName; + else { + scope += readInfrastructureName(); + _log.debug("infrastrucute name is null, setting root scope=" + scope); + } + String currScope = ScopeProvider.instance.get(); + ScopeProvider.instance.set(scope); + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_RESOURCE_NAME +"'"); + query.addCondition("$resource/Profile/Platform/Name/text() eq '"+ PLATFORM_NAME +"'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + ScopeProvider.instance.set(currScope); + return toReturn; + } + + private String readInfrastructureName() { + + Properties props = new Properties(); + try { + StringBuilder sb = new StringBuilder(getCatalinaHome()); + sb.append(File.separator) + .append(PortalContext.CONFIGURATION_FOLDER) + .append(File.separator) + .append(PortalContext.INFRA_PROPERTY_FILENAME); + String propertyfile = sb.toString(); + File propsFile = new File(propertyfile); + FileInputStream fis = new FileInputStream(propsFile); + props.load( fis); + return props.getProperty(GCubePortalConstants.INFRASTRUCTURE_NAME); + } + catch(IOException e) { + _log.error("infrastructure.properties file not found under $CATALINA_HOME/conf/ dir, setting default infrastructure Name " + "gcube"); + return "gcube"; + } + } + + + /** + * + */ + private void loadDefaultConfiguration() { + Properties props = new Properties(); + try { + props.load(CassandraClusterConnection.class.getResourceAsStream(DEFAULT_CONFIGURATION)); + host = props.getProperty(HOST_PROPERTY) + ":" + props.getProperty(HOST_PORT_PROPERTY); + datacenterName = props.getProperty(DATACENTER_NAME_PROPERTY); + keyspaceName = props.getProperty(KEY_SPACE_NAME_PROPERTY); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + + public String getKeyspaceName() { + return keyspaceName; + } + + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @Override + public String toString() { + return "RunningCluster [host=" + host + ", datacenterName=" + datacenterName + + ", keyspaceName=" + keyspaceName + "]"; + } + /** + * + * @return $CATALINA_HOME + */ + private static String getCatalinaHome() { + return (System.getenv("CATALINA_HOME").endsWith("/") ? System.getenv("CATALINA_HOME") : System.getenv("CATALINA_HOME")+"/"); + } + public void setDatacenterName(String datacenterName){ + this.datacenterName = datacenterName; + } + + public String getDatacenterName() { + return datacenterName; + } + + public List getHosts() { + List hosts = new ArrayList<>(); + String [] ips = host.split(", "); + for (String ip: ips){ + String[] ip_port = ip.split(":"); + hosts.add(new InetSocketAddress(ip_port[0], Integer.parseInt(ip_port[1]))); + } + return hosts; + } +} \ No newline at end of file diff --git a/src/main/java/org/gcube/social_networking/server/CassandraClusterConnection.java b/src/main/java/org/gcube/social_networking/server/CassandraClusterConnection.java new file mode 100644 index 0000000..101b069 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/server/CassandraClusterConnection.java @@ -0,0 +1,501 @@ +package org.gcube.social_networking.server; + +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.List; + + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.CqlSessionBuilder; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverConfigLoader; +import com.datastax.oss.driver.api.core.cql.ResultSet; +import com.datastax.oss.driver.api.core.metadata.Metadata; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.querybuilder.SchemaBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Massimiliano Assante ISTI-CNR + * @author Ahmed Ibrahim ISTI-CNR + * + */ +class CassandraClusterConnection { + /** + * logger + */ + private static final Logger _log = LoggerFactory.getLogger(CassandraClusterConnection.class); + + /** + * keyspace location + */ + private static List hosts; + private static String datacenterName; + private static String keyspaceName; + private CqlSession myKeyspaceSession; + + /** + * + * @param dropSchema set true if you want do drop the current and set up new one + * the connection to cassandra cluster + */ + protected CassandraClusterConnection(boolean dropSchema) throws Exception { + if (hosts == null || datacenterName == null || keyspaceName == null) { + CassandraCluster cluster = CassandraCluster.getInstance(null); + + hosts = cluster.getHosts(); + datacenterName = cluster.getDatacenterName(); + keyspaceName = cluster.getKeyspaceName(); + } + + _log.info(keyspaceName + " KeySpace SetUp ..."); + SetUpKeySpaces(dropSchema); + myKeyspaceSession = connect(keyspaceName); + _log.info("CONNECTED! using KeySpace: " + keyspaceName); + } + + /** + * + * @param dropSchema set true if you want to drop the current and set up new one + * the connection to cassandra cluster + */ + protected CassandraClusterConnection(boolean dropSchema, String infrastructureName) throws Exception { + if (hosts == null || datacenterName == null || keyspaceName == null) { + CassandraCluster cluster = CassandraCluster.getInstance(infrastructureName); + hosts = cluster.getHosts(); + datacenterName = cluster.getDatacenterName(); + keyspaceName = cluster.getKeyspaceName(); + } + _log.info(keyspaceName + " KeySpace SetUp ..."); + SetUpKeySpaces(dropSchema); + myKeyspaceSession = connect(keyspaceName); + _log.info("CONNECTED! using KeySpace: " + keyspaceName); + } + + public CqlSession getKeyspaceSession(){ + if (myKeyspaceSession.isClosed()){ + myKeyspaceSession = connect(keyspaceName); + } + return myKeyspaceSession; + } + + /** + * @param dropSchema set true if you want to drop the current and set up new one + * the connection to cassandra cluster + */ + public void SetUpKeySpaces(boolean dropSchema) { + boolean createNew = false; + boolean found = false; + CqlSession session = connect(); + Metadata metaData = session.getMetadata(); + for (KeyspaceMetadata meta : metaData.getKeyspaces().values()) { + if (meta.getName().toString().equals(keyspaceName)){ + found = true; + break; + } + } + try { + if (dropSchema && found) { + _log.info("Dropping Keyspace: " + keyspaceName + " ..."); + try { + ResultSet returned = dropKeyspace(); + Thread.sleep(2000); + if (returned.wasApplied()) + _log.info("Dropped " + keyspaceName); + else + _log.info("Couldn't drop " + keyspaceName); + } catch (Exception e) { + _log.error("Dropping Keyspace operation Failed ... " + keyspaceName + " does NOT exists"); + return; + } + createNew = true; + } + + if (!found || createNew) { + _log.info("Keyspace does not exist, triggering schema creation ... "); + int replicationFactor = 2; + createKeyspace(keyspaceName, replicationFactor); + closeSession(session); + createTables(); + _log.info("Using Keyspace " + keyspaceName); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /* + * + ********************** CASSANDRA KEYSPACE CREATION *********************** + * + */ + private static CqlSession connect() { + CqlSession cqlSession = configBuilder(CqlSession.builder()) + .addContactPoints(hosts) + .withLocalDatacenter(datacenterName) + .build(); + _log.info("[OK] Connected to Cassandra Cluster"); + return cqlSession; + } + private static CqlSession connect(String KEYSPACE_NAME) { + CqlSession cqlSession = configBuilder(CqlSession.builder()) + .addContactPoints(hosts) + .withKeyspace(KEYSPACE_NAME) + .withLocalDatacenter(datacenterName) + .build(); + _log.info("[OK] Connected to Keyspace {} ", KEYSPACE_NAME); + return cqlSession; + } + + public static void closeSession(CqlSession session) { + if (session != null) session.close(); + _log.info("[OK]Session is now closed"); + } + + public void closeConnection(){ + if(!myKeyspaceSession.isClosed()){ + try{ + _log.info("Closing connection"); + closeSession(myKeyspaceSession); + _log.info("Connection closed!"); + }catch(Exception e){ + _log.error("Unable to close connection", e); + } + } + } + + private static CqlSessionBuilder configBuilder(CqlSessionBuilder cqlSessionBuilder){ + return cqlSessionBuilder + .withConfigLoader(DriverConfigLoader.programmaticBuilder() + // Resolves the timeout query 'SELECT * FROM system_schema.tables' timed out after PT2S + .withDuration(DefaultDriverOption.METADATA_SCHEMA_REQUEST_TIMEOUT, Duration.ofMillis(240000)) + .withDuration(DefaultDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, Duration.ofMillis(240000)) + .withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofMillis(240000)) + .build()); + } + private static void createKeyspace(String keyspaceName, int replicationFactor) { + try (CqlSession cqlSession = configBuilder(CqlSession.builder()) + .addContactPoints(hosts) + .withLocalDatacenter(datacenterName) + .build()) { + cqlSession.execute(SchemaBuilder.createKeyspace(keyspaceName) + .ifNotExists() + .withSimpleStrategy(replicationFactor) + .withDurableWrites(true) + .build()); + _log.info("+ Keyspace '{}' created.", keyspaceName); + closeSession(cqlSession); + } + } + + private static ResultSet dropKeyspace(){ + ResultSet toreturn; + try (CqlSession cqlSession = configBuilder(CqlSession.builder()) + .addContactPoints(hosts) + .withLocalDatacenter(datacenterName) + .build()) { + toreturn = cqlSession.execute(SchemaBuilder.dropKeyspace(keyspaceName).ifExists().build()); + _log.info("Keyspace {} dropped.", keyspaceName); + closeSession(cqlSession); + } + return toreturn; + } + private void createTables(){ + try (CqlSession cqlSession = configBuilder(CqlSession.builder()) + .addContactPoints(hosts) + .withLocalDatacenter(datacenterName) + .withKeyspace(keyspaceName) + .build()) { + + createTableUSERNotificationsPreferences(cqlSession); + createTableUSERNotifications(cqlSession); + createTableVRETimeline(cqlSession); + createTableAppTimeline(cqlSession); + createTableUSERTimeline(cqlSession); + createTableHashtaggedPosts(cqlSession); + createTableHashtaggedComments(cqlSession); + createTableHashtagsCounter(cqlSession); + createTableUSERNotificationsUnread(cqlSession); + createTableUSERLikes(cqlSession); + createTableVREInvites(cqlSession); + createTableEMAILInvites(cqlSession); + createTableAttachments(cqlSession); + createTableInvites(cqlSession); + createTableLikes(cqlSession); + createTableComments(cqlSession); + createTableNotifications(cqlSession); + createTablePosts(cqlSession); + + closeSession(cqlSession); + } + } + private void createTableUSERNotificationsPreferences(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("UserNotificationsPreferences") + .ifNotExists() + .withPartitionKey("userid", DataTypes.TEXT) + .withPartitionKey("type", DataTypes.TEXT) + .withColumn("preference", DataTypes.TEXT) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "USERNotificationsPreferences"); + } + private void createTableUSERNotifications(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("UserNotifications") + .ifNotExists() + .withPartitionKey("userid", DataTypes.TEXT) + .withPartitionKey("timestamp", DataTypes.TIMESTAMP) + .withColumn("notid", DataTypes.UUID) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "USERNotifications"); + } + private void createTableVRETimeline(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("VRETimeline") + .ifNotExists() + .withPartitionKey("vreid", DataTypes.TEXT) + .withPartitionKey("timestamp", DataTypes.TIMESTAMP) + .withColumn("postid", DataTypes.UUID) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "VRETimeline"); + } + private void createTableAppTimeline(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("AppTimeline") + .ifNotExists() + .withPartitionKey("appid", DataTypes.TEXT) + .withPartitionKey("timestamp", DataTypes.TIMESTAMP) + .withColumn("postid", DataTypes.UUID) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "AppTimeline"); + } + private void createTableUSERTimeline(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("UserTimeline") + .ifNotExists() + .withPartitionKey("userid", DataTypes.TEXT) + .withPartitionKey("timestamp", DataTypes.TIMESTAMP) + .withColumn("postid", DataTypes.UUID) + .withCompactStorage() + .build()); + _log.info("+ Table '{}' has been created (if needed).", "USERTimeline"); + } + private void createTableHashtaggedPosts(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("HashtaggedPosts") + .ifNotExists() + .withPartitionKey("hashtag", DataTypes.TEXT) + .withPartitionKey("postid", DataTypes.UUID) + .withColumn("vreid", DataTypes.TEXT) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "HashtaggedPosts"); + } + private void createTableHashtaggedComments(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("HashtaggedComments") + .ifNotExists() + .withPartitionKey("hashtag", DataTypes.TEXT) + .withPartitionKey("commentid", DataTypes.UUID) + .withColumn("vreid", DataTypes.TEXT) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "HashtaggedComments"); + } + private void createTableHashtagsCounter(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("HashtagsCounter") + .ifNotExists() + .withPartitionKey("vreid", DataTypes.TEXT) + .withPartitionKey("hashtag", DataTypes.TEXT) + .withColumn("count", DataTypes.BIGINT) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "HashtagsCounter"); + } + private void createTableUSERNotificationsUnread(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("UserUnreadNotifications") + .ifNotExists() + .withPartitionKey("userid", DataTypes.TEXT) + .withPartitionKey("timestamp", DataTypes.TIMESTAMP) + .withColumn("notid", DataTypes.UUID) + .withCompactStorage() + .build()); + _log.info("+ Table '{}' has been created (if needed).", "USERNotificationsUnread"); + } + private void createTableUSERLikes(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("UserLikes") + .ifNotExists() + .withPartitionKey("userid", DataTypes.TEXT) + .withPartitionKey("likeid", DataTypes.UUID) + .withColumn("postid", DataTypes.UUID) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "USERLikes"); + } + private void createTableVREInvites(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("VREInvites") + .ifNotExists() + .withPartitionKey("vreid", DataTypes.TEXT) + .withPartitionKey("inviteid", DataTypes.UUID) + .withColumn("status", DataTypes.TEXT) + .withCompactStorage() + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "VREInvites"); + } + private void createTableEMAILInvites(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("EmailInvites") + .ifNotExists() + .withPartitionKey("email", DataTypes.TEXT) + .withPartitionKey("vreid", DataTypes.TEXT) + .withColumn("inviteid", DataTypes.UUID) + .withCompactStorage() + .build()); + _log.info("+ Table '{}' has been created (if needed).", "EMAILInvites"); + } + private void createTableAttachments(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("Attachments") + .ifNotExists() + .withPartitionKey("attachid", DataTypes.UUID) + .withColumn("postid", DataTypes.UUID) + .withColumn("uri", DataTypes.TEXT) + .withColumn("name", DataTypes.TEXT) + .withColumn("description", DataTypes.TEXT) + .withColumn("urithumbnail", DataTypes.TEXT) + .withColumn("mimetype", DataTypes.TEXT) + .withCompactStorage() + .build()); + cqlSession.execute(SchemaBuilder.createIndex("post_attach") + .ifNotExists() + .onTable("Attachments") + .andColumn("postid") + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "Attachments"); + } + private void createTableInvites(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("Invites") + .ifNotExists() + .withPartitionKey("inviteid", DataTypes.UUID) + .withColumn("senderuserid", DataTypes.TEXT) + .withColumn("vreid", DataTypes.TEXT) + .withColumn("email", DataTypes.TEXT) + .withColumn("controlcode", DataTypes.TEXT) + .withColumn("status", DataTypes.TEXT) + .withColumn("timestamp", DataTypes.TIMESTAMP) + .withColumn("senderfullname", DataTypes.TEXT) + .withCompactStorage() + .build()); + cqlSession.execute(SchemaBuilder.createIndex("sender") + .ifNotExists() + .onTable("Invites") + .andColumn("senderuserid") + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "Invites"); + } + private void createTableLikes(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("Likes") + .ifNotExists() + .withPartitionKey("likeid", DataTypes.UUID) + .withColumn("userid", DataTypes.TEXT) + .withColumn("fullname", DataTypes.TEXT) + .withColumn("thumbnailurl", DataTypes.TEXT) + .withColumn("postid", DataTypes.UUID) + .withColumn("timestamp", DataTypes.TIMESTAMP) + .withCompactStorage() + .build()); + cqlSession.execute(SchemaBuilder.createIndex("post_likes") + .ifNotExists() + .onTable("Likes") + .andColumn("postid") + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "Likes"); + } + private void createTableComments(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("Comments") + .ifNotExists() + .withPartitionKey("commentid", DataTypes.UUID) + .withColumn("userid", DataTypes.TEXT) + .withColumn("fullname", DataTypes.TEXT) + .withColumn("thumbnailurl", DataTypes.TEXT) + .withColumn("comment", DataTypes.TEXT) + .withColumn("postid", DataTypes.UUID) + .withColumn("timestamp", DataTypes.TIMESTAMP) + .withColumn("isedit", DataTypes.BOOLEAN) + .withColumn("lastedittime", DataTypes.TIMESTAMP) + .withCompactStorage() + .build()); + cqlSession.execute(SchemaBuilder.createIndex("post_comments") + .ifNotExists() + .onTable("Comments") + .andColumn("postid") + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "Comments"); + } + private void createTableNotifications(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("Notifications") + .ifNotExists() + .withPartitionKey("notid", DataTypes.UUID) + .withColumn("type", DataTypes.TEXT) + .withColumn("userid", DataTypes.TEXT) + .withColumn("subjectid", DataTypes.TEXT) + .withColumn("timestamp", DataTypes.TIMESTAMP) + .withColumn("description", DataTypes.TEXT) + .withColumn("uri", DataTypes.TEXT) + .withColumn("senderid", DataTypes.TEXT) + .withColumn("senderfullname", DataTypes.TEXT) + .withColumn("senderthumbnailurl", DataTypes.TEXT) + .withColumn("isread", DataTypes.BOOLEAN) + .withCompactStorage() + .build()); + cqlSession.execute(SchemaBuilder.createIndex("not_type") + .ifNotExists() + .onTable("Notifications") + .andColumn("type") + .build()); + _log.info("+ Table '{}' has been created (if needed).", "Notifications"); + } + private void createTablePosts(CqlSession cqlSession) { + cqlSession.execute(SchemaBuilder.createTable("Posts") + .ifNotExists() + .withPartitionKey("postid", DataTypes.UUID) + .withColumn("linkhost", DataTypes.TEXT) + .withColumn("description", DataTypes.TEXT) + .withColumn("email", DataTypes.TEXT) + .withColumn("likesno", DataTypes.BIGINT) + .withColumn("thumbnailurl", DataTypes.TEXT) + .withColumn("linkdescription", DataTypes.TEXT) + .withColumn("timestamp", DataTypes.TIMESTAMP) + .withColumn("uri", DataTypes.TEXT) + .withColumn("isapplicationpost", DataTypes.BOOLEAN) + .withColumn("entityid", DataTypes.TEXT) + .withColumn("privacy", DataTypes.TEXT) + .withColumn("type", DataTypes.TEXT) + .withColumn("urithumbnail", DataTypes.TEXT) + .withColumn("vreid", DataTypes.TEXT) + .withColumn("multifileupload", DataTypes.BOOLEAN) + .withColumn("fullname", DataTypes.TEXT) + .withColumn("commentsno", DataTypes.BIGINT) + .withColumn("linktitle", DataTypes.TEXT) + .withCompactStorage() + .build()); + cqlSession.execute(SchemaBuilder.createIndex("posts_privacy") + .ifNotExists() + .onTable("Posts") + .andColumn("privacy") + .build()); + + _log.info("+ Table '{}' has been created (if needed).", "Posts"); + } + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/social_networking/server/CassandraConnection.java b/src/main/java/org/gcube/social_networking/server/CassandraConnection.java new file mode 100644 index 0000000..22886ed --- /dev/null +++ b/src/main/java/org/gcube/social_networking/server/CassandraConnection.java @@ -0,0 +1,49 @@ +package org.gcube.social_networking.server; + + +import org.gcube.smartgears.ContextProvider; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.slf4j.LoggerFactory; + +/** + * Cassandra connection class. + * @author Costantino Perciante at ISTI-CNR + */ +public class CassandraConnection { + + // Logger + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CassandraConnection.class); + + // databook store (singleton) + private static SocialDBDriver store; + + private static CassandraConnection singleton = new CassandraConnection(); + + private CassandraConnection(){ + ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears + logger.info("Getting connection to cassandra"); + store = new SocialDBDatastaxDriver(ctx.container().configuration().infrastructure()); + logger.info("Connection to cassandra created"); + } + + /** + * Returns the object to query the cassandra cluster. + * @return connection pool to cassandra cluster + */ + public SocialDBDriver getDatabookStore(){ + + return store; + + } + + /** + * Get the instance + * @return + */ + public static CassandraConnection getInstance(){ + + return singleton; + + } + +} diff --git a/src/main/java/org/gcube/social_networking/server/SocialDBDatastaxDriver.java b/src/main/java/org/gcube/social_networking/server/SocialDBDatastaxDriver.java new file mode 100644 index 0000000..df4ba1a --- /dev/null +++ b/src/main/java/org/gcube/social_networking/server/SocialDBDatastaxDriver.java @@ -0,0 +1,3020 @@ +package org.gcube.social_networking.server; + +import java.time.Instant; +import java.util.*; + +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.*; +import com.datastax.oss.driver.api.querybuilder.QueryBuilder; +import org.apache.commons.lang.NullArgumentException; +import org.gcube.social_networking.socialnetworking.model.shared.*; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.gcube.social_networking.utils.Schema.*; +/** + * @author Massimiliano Assante ISTI-CNR + * @author Costantino Perciante ISTI-CNR + * @author Ahmed Ibrahim ISTI-CNR + * This class is used for querying and adding data to Cassandra via Datastax High Level API + */ +public final class SocialDBDatastaxDriver implements SocialDBDriver { + + /** + * logger + */ + private static final Logger _log = LoggerFactory.getLogger(SocialDBDatastaxDriver.class); + + /** + * connection instance + */ + private CassandraClusterConnection conn; + + protected CassandraClusterConnection getConnection() { + return conn; + } + /** + * use this constructor carefully from test classes + * @param dropSchema set true if you want do drop the current and set up new one + */ + protected SocialDBDatastaxDriver(boolean dropSchema) { + try { + conn = new CassandraClusterConnection(dropSchema); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** + * public constructor, no dropping schema is allowed + */ + public SocialDBDatastaxDriver() { + try { + conn = new CassandraClusterConnection(false); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * public constructor, no dropping schema is allowed, infrastructureName is given. + */ + public SocialDBDatastaxDriver(String infrastructureName) { + try { + conn = new CassandraClusterConnection(false, infrastructureName); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /* + Utility Functions + */ + private static boolean existRecordbyId(CqlSession session, String id, String tableName, String colName) { + PreparedStatement stmt = session.prepare(QueryBuilder + .selectFrom(tableName).column(colName) + .whereColumn(colName) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + return session.execute(stmt.bind(id)).getAvailableWithoutFetching() > 0; + } + + private static boolean existRecordbyCompId(CqlSession session, String col1, String col2, String id1, String id2, String tableName) { + PreparedStatement stmt = session.prepare(QueryBuilder + .selectFrom(tableName).all() + .whereColumn(col1) + .isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(col2) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + return session.execute(stmt.bind(id1, id2)).getAvailableWithoutFetching() > 0; + } + + private static PreparedStatement updatePostEntry(CqlSession session, String colName){ + return session.prepare(QueryBuilder.update(POSTS) + .setColumn(colName, QueryBuilder.bindMarker()) + .whereColumn(POST_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static Attachment readAttachmentFromRow(Row record) { + Attachment a = new Attachment(); + a.setId(Objects.requireNonNull(record.getUuid(ATTACH_ID)).toString()); + a.setUri(record.getString(URI)); + a.setName(record.getString(NAME)); + a.setDescription(record.getString(DESCRIPTION)); + a.setThumbnailURL(record.getString(URI_THUMBNAIL)); + a.setMimeType(record.getString(MIME_TYPE)); + return a; + } + private static Notification readNotificationFromRow(Row record) throws NotificationTypeNotFoundException { + Notification a = new Notification(); + a.setKey(Objects.requireNonNull(record.getUuid(NOT_ID)).toString()); + a.setType(getNotificationType(Objects.requireNonNull(record.getString(TYPE)))); + a.setUserid(record.getString(USER_ID)); + a.setSubjectid(record.getString(SUBJECT_ID)); + a.setTime(Date.from(Objects.requireNonNull(record.getInstant(TIMESTAMP)))); + a.setUri(record.getString(URI)); + a.setDescription(record.getString(DESCRIPTION)); + a.setRead(record.getBoolean(IS_READ)); + a.setSenderid(record.getString(SENDER_ID)); + a.setSenderFullName(record.getString(SENDER_FULL_NAME)); + a.setSenderThumbnail(record.getString(SENDER_THUMBNAIL_URL)); + return a; + } + private static Post readPostFromRow(Row record) throws PostTypeNotFoundException, PrivacyLevelTypeNotFoundException { + Post a = new Post(); + + a.setKey(Objects.requireNonNull(record.getUuid(POST_ID)).toString()); + a.setType(getPostType(Objects.requireNonNull(record.getString(TYPE)))); + a.setEntityId(record.getString(ENTITY_ID)); + a.setTime(Date.from(Objects.requireNonNull(record.getInstant(TIMESTAMP)))); + a.setVreid(record.getString(VRE_ID)); + a.setUri(record.getString(URI)); + a.setUriThumbnail(record.getString(URI_THUMBNAIL)); + a.setDescription(record.getString(DESCRIPTION)); + a.setPrivacy(getPrivacyLevel(Objects.requireNonNull(record.getString(PRIVACY)))); + a.setFullName(record.getString(FULL_NAME)); + a.setEmail(record.getString(EMAIL)); + a.setThumbnailURL(record.getString(THUMBNAIL_URL)); + a.setCommentsNo(String.valueOf(record.getLong(COMMENTS_NO))); + a.setLikesNo(String.valueOf(record.getLong(LIKES_NO))); + a.setLinkDescription(record.getString(LINK_DESCRIPTION)); + a.setLinkTitle(record.getString(LINK_TITLE)); + a.setLinkHost(record.getString(LINK_HOST)); + a.setApplicationPost(record.getBoolean(IS_APPLICATION_POST)); + a.setMultiFileUpload(record.getBoolean(MULTI_FILE_UPLOAD)); + + return a; + } + private static Like readLikeFromRow(Row record) { + Like a = new Like(); + a.setKey(Objects.requireNonNull(record.getUuid(LIKE_ID)).toString()); + a.setUserid(record.getString(USER_ID)); + a.setTime(Date.from(Objects.requireNonNull(record.getInstant(TIMESTAMP)))); + a.setPostid(Objects.requireNonNull(record.getUuid(POST_ID)).toString()); + a.setFullName(record.getString(FULL_NAME)); + a.setThumbnailURL(record.getString(THUMBNAIL_URL)); + return a; + } + private static Comment readCommentFromRow(Row record) { + Comment a = new Comment(); + a.setKey(Objects.requireNonNull(record.getUuid(COMMENT_ID)).toString()); + a.setUserid(record.getString(USER_ID)); + a.setTime(Date.from(Objects.requireNonNull(record.getInstant(TIMESTAMP)))); + a.setPostid(Objects.requireNonNull(record.getUuid(POST_ID)).toString()); + a.setFullName(record.getString(FULL_NAME)); + a.setThumbnailURL(record.getString(THUMBNAIL_URL)); + a.setText(record.getString(COMMENT)); + a.setEdit(!record.isNull(IS_EDIT) && record.getBoolean(IS_EDIT)); + a.setLastEditTime(record.isNull(LAST_EDIT_TIME)? null : Date.from(Objects.requireNonNull(record.getInstant(LAST_EDIT_TIME)))); + return a; + } + private static Invite readAInviteFromRow(Row record) throws InviteStatusNotFoundException { + Invite a = new Invite(); + a.setKey(Objects.requireNonNull(record.getUuid(INVITE_ID)).toString()); + a.setSenderUserId(record.getString(SENDER_USER_ID)); + a.setVreid(record.getString(VRE_ID)); + a.setInvitedEmail(record.getString(EMAIL)); + a.setControlCode(record.getString(CONTROL_CODE)); + a.setStatus(getInviteStatusType(Objects.requireNonNull(record.getString(STATUS)))); + a.setTime(Date.from(Objects.requireNonNull(record.getInstant(TIMESTAMP)))); + a.setSenderFullName(record.getString(SENDER_FULL_NAME)); + return a; + } + + private static Optional < Post > findPostById(String postid, CqlSession session) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException { + PreparedStatement stmtFindPost = session.prepare(QueryBuilder + .selectFrom(POSTS).all() + .whereColumn(POST_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + ResultSet rs = session.execute(stmtFindPost.bind(UUID.fromString(postid))); + // We query by the primary key ensuring unicity + Row record = rs.one(); + return (null != record) ? Optional.of(readPostFromRow(record)) :Optional.empty(); + } + private static Optional < Notification > findNotById(String notid, CqlSession session) throws NotificationTypeNotFoundException { + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(NOTIFICATIONS).all() + .whereColumn(NOT_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + ResultSet rs = session.execute(stmtFind.bind(UUID.fromString(notid))); + // We query by the primary key ensuring unicity + Row record = rs.one(); + return (null != record) ? Optional.of(readNotificationFromRow(record)) :Optional.empty(); + } + + + private static PreparedStatement updateInviteEntry(CqlSession session, String colName){ + return session.prepare(QueryBuilder.update(INVITES) + .setColumn(colName, QueryBuilder.bindMarker()) + .whereColumn(INVITE_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement updateVreInviteEntry(CqlSession session, String colName){ + return session.prepare(QueryBuilder.update(VRE_INVITES) + .setColumn(colName, QueryBuilder.bindMarker()) + .whereColumn(VRE_ID).isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(INVITE_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement deleteHashtagEntry(CqlSession session){ + return session.prepare(QueryBuilder.deleteFrom(HASHTAGGED_POSTS) + .whereColumn(HASHTAG) + .isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(POST_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement deleteHashtagCommentEntry(CqlSession session){ + return session.prepare(QueryBuilder.deleteFrom(HASHTAGGED_COMMENTS) + .whereColumn(HASHTAG) + .isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(COMMENT_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement deleteLikeEntry(CqlSession session){ + return session.prepare(QueryBuilder.deleteFrom(LIKES) + .whereColumn(LIKE_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement deleteUserLikeEntry(CqlSession session){ + return session.prepare(QueryBuilder.deleteFrom(USER_LIKED_POSTS) + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(LIKE_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement updateCommentEntry(CqlSession session, String colName){ + return session.prepare(QueryBuilder.update(COMMENTS) + .setColumn(colName, QueryBuilder.bindMarker()) + .whereColumn(COMMENT_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement deleteCommentEntry(CqlSession session){ + return session.prepare(QueryBuilder.deleteFrom(COMMENTS) + .whereColumn(COMMENT_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement updateNotificationEntry(CqlSession session, String colName){ + return session.prepare(QueryBuilder.update(NOTIFICATIONS) + .setColumn(colName, QueryBuilder.bindMarker()) + .whereColumn(NOT_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement deleteUnreadNotEntry(CqlSession session){ + return session.prepare(QueryBuilder.deleteFrom(USER_NOTIFICATIONS_UNREAD) + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(TIMESTAMP) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + } + private static BatchStatement getBatch(){ + return BatchStatement.builder(BatchType.LOGGED).build(); + } + + private static PreparedStatement createPostEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(POSTS) + .value(POST_ID, QueryBuilder.bindMarker()) + .value(LINK_HOST, QueryBuilder.bindMarker()) + .value(DESCRIPTION, QueryBuilder.bindMarker()) + .value(EMAIL, QueryBuilder.bindMarker()) + .value(LIKES_NO, QueryBuilder.bindMarker()) + .value(THUMBNAIL_URL, QueryBuilder.bindMarker()) + .value(LINK_DESCRIPTION, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(URI, QueryBuilder.bindMarker()) + .value(IS_APPLICATION_POST, QueryBuilder.bindMarker()) + .value(ENTITY_ID, QueryBuilder.bindMarker()) + .value(PRIVACY, QueryBuilder.bindMarker()) + .value(TYPE, QueryBuilder.bindMarker()) + .value(URI_THUMBNAIL, QueryBuilder.bindMarker()) + .value(VRE_ID, QueryBuilder.bindMarker()) + .value(MULTI_FILE_UPLOAD, QueryBuilder.bindMarker()) + .value(FULL_NAME, QueryBuilder.bindMarker()) + .value(COMMENTS_NO, QueryBuilder.bindMarker()) + .value(LINK_TITLE, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createUserTimelineEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(USER_TIMELINE_POSTS) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createVreTimelineEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(VRE_TIMELINE_POSTS) + .value(VRE_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createAppTimelineEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(APP_TIMELINE_POSTS) + .value(APP_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNotificationEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(NOTIFICATIONS) + .value(NOT_ID, QueryBuilder.bindMarker()) + .value(TYPE, QueryBuilder.bindMarker()) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(SUBJECT_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(DESCRIPTION, QueryBuilder.bindMarker()) + .value(URI, QueryBuilder.bindMarker()) + .value(SENDER_ID, QueryBuilder.bindMarker()) + .value(SENDER_FULL_NAME, QueryBuilder.bindMarker()) + .value(SENDER_THUMBNAIL_URL, QueryBuilder.bindMarker()) + .value(IS_READ, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createUserNotificationsEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(USER_NOTIFICATIONS) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(NOT_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createUnreadNotificationEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(USER_NOTIFICATIONS_UNREAD) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(NOT_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNotificationPreferenceEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(USER_NOTIFICATIONS_PREFERENCES) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(TYPE, QueryBuilder.bindMarker()) + .value(PREFERENCE, QueryBuilder.bindMarker()) + .build()); + } + + private static PreparedStatement createNewCommentEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(COMMENTS) + .value(COMMENT_ID, QueryBuilder.bindMarker()) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(FULL_NAME, QueryBuilder.bindMarker()) + .value(THUMBNAIL_URL, QueryBuilder.bindMarker()) + .value(COMMENT, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(IS_EDIT, QueryBuilder.bindMarker()) + .value(LAST_EDIT_TIME, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewLikeEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(LIKES) + .value(LIKE_ID, QueryBuilder.bindMarker()) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(FULL_NAME, QueryBuilder.bindMarker()) + .value(THUMBNAIL_URL, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewUserLikesEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(USER_LIKED_POSTS) + .value(USER_ID, QueryBuilder.bindMarker()) + .value(LIKE_ID, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewHashtagTimelineEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(HASHTAGGED_POSTS) + .value(HASHTAG, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .value(VRE_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewHashtagCommentEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(HASHTAGGED_COMMENTS) + .value(HASHTAG, QueryBuilder.bindMarker()) + .value(COMMENT_ID, QueryBuilder.bindMarker()) + .value(VRE_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewEmailInviteEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(EMAIL_INVITES) + .value(EMAIL, QueryBuilder.bindMarker()) + .value(VRE_ID, QueryBuilder.bindMarker()) + .value(INVITE_ID, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewInviteEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(INVITES) + .value(INVITE_ID, QueryBuilder.bindMarker()) + .value(SENDER_USER_ID, QueryBuilder.bindMarker()) + .value(VRE_ID, QueryBuilder.bindMarker()) + .value(EMAIL, QueryBuilder.bindMarker()) + .value(CONTROL_CODE, QueryBuilder.bindMarker()) + .value(STATUS, QueryBuilder.bindMarker()) + .value(TIMESTAMP, QueryBuilder.bindMarker()) + .value(SENDER_FULL_NAME, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewVreInviteEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(VRE_INVITES) + .value(VRE_ID, QueryBuilder.bindMarker()) + .value(INVITE_ID, QueryBuilder.bindMarker()) + .value(STATUS, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewaAttachEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(ATTACHMENTS) + .value(ATTACH_ID, QueryBuilder.bindMarker()) + .value(POST_ID, QueryBuilder.bindMarker()) + .value(URI, QueryBuilder.bindMarker()) + .value(NAME, QueryBuilder.bindMarker()) + .value(DESCRIPTION, QueryBuilder.bindMarker()) + .value(URI_THUMBNAIL, QueryBuilder.bindMarker()) + .value(MIME_TYPE, QueryBuilder.bindMarker()) + .build()); + } + private static PreparedStatement createNewUHashtagCounterEntry(CqlSession session){ + return session.prepare( + QueryBuilder.insertInto(HASHTAGS_COUNTER) + .value(VRE_ID, QueryBuilder.bindMarker()) + .value(HASHTAG, QueryBuilder.bindMarker()) + .value(COUNT, QueryBuilder.bindMarker()) + .build()); + } + /* + * + ********************** POSTS *********************** + * + */ + private List insertIntoPosts(CqlSession session, Post post){ + PreparedStatement prepStmt1 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(LINK_HOST, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt2 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(DESCRIPTION, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt3 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(EMAIL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt4 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(LIKES_NO, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt5 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(THUMBNAIL_URL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt6 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(LINK_DESCRIPTION, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt7 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(TIMESTAMP, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt8 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(URI, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt9 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(IS_APPLICATION_POST, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt10 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(ENTITY_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt11 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(PRIVACY, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt12 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(TYPE, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt13 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(URI_THUMBNAIL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt14 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(VRE_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt15 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(MULTI_FILE_UPLOAD, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt16 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(FULL_NAME, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt17 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(COMMENTS_NO, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt18 = session.prepare(QueryBuilder.insertInto(POSTS).value(POST_ID, QueryBuilder.bindMarker()).value(LINK_TITLE, QueryBuilder.bindMarker()).build()); + List boundStatements = new ArrayList<>(); + + if(post.getLinkHost()!=null){ + boundStatements.add(prepStmt1.bind(UUID.fromString(post.getKey()), post.getLinkHost())); + }if(post.getDescription()!=null){ + boundStatements.add(prepStmt2.bind(UUID.fromString(post.getKey()), post.getDescription())); + }if(post.getEmail()!=null){ + boundStatements.add(prepStmt3.bind(UUID.fromString(post.getKey()), post.getEmail())); + }if(post.getLikesNo()!=null){ + boundStatements.add(prepStmt4.bind(UUID.fromString(post.getKey()), Long.parseLong(post.getLikesNo()))); + }if(post.getThumbnailURL()!=null){ + boundStatements.add(prepStmt5.bind(UUID.fromString(post.getKey()), post.getThumbnailURL())); + }if(post.getLinkDescription()!=null){ + boundStatements.add(prepStmt6.bind(UUID.fromString(post.getKey()), post.getLinkDescription())); + }if(post.getTime()!=null){ + boundStatements.add(prepStmt7.bind(UUID.fromString(post.getKey()), post.getTime().toInstant())); + }if(post.getUri()!=null){ + boundStatements.add(prepStmt8.bind(UUID.fromString(post.getKey()), post.getUri())); + } + boundStatements.add(prepStmt9.bind(UUID.fromString(post.getKey()), post.isApplicationPost())); + if(post.getEntityId()!=null){ + boundStatements.add(prepStmt10.bind(UUID.fromString(post.getKey()), post.getEntityId())); + }if(post.getPrivacy()!=null){ + boundStatements.add(prepStmt11.bind(UUID.fromString(post.getKey()), post.getPrivacy().toString())); + }if(post.getType()!=null){ + boundStatements.add(prepStmt12.bind(UUID.fromString(post.getKey()), post.getType().toString())); + }if(post.getUriThumbnail()!=null){ + boundStatements.add(prepStmt13.bind(UUID.fromString(post.getKey()), post.getUriThumbnail())); + }if(post.getVreid()!=null){ + boundStatements.add(prepStmt14.bind(UUID.fromString(post.getKey()), post.getVreid())); + } + boundStatements.add(prepStmt15.bind(UUID.fromString(post.getKey()), post.isMultiFileUpload())); + if(post.getFullName()!=null){ + boundStatements.add(prepStmt16.bind(UUID.fromString(post.getKey()), post.getFullName())); + }if(post.getCommentsNo()!=null){ + boundStatements.add(prepStmt17.bind(UUID.fromString(post.getKey()), Long.parseLong(post.getCommentsNo()))); + }if(post.getLinkTitle()!=null){ + boundStatements.add(prepStmt18.bind(UUID.fromString(post.getKey()), post.getLinkTitle())); + } + return boundStatements; + } + /** + * @inheritDoc + */ + @Override + public boolean saveUserPost(Post post) { + CqlSession session = conn.getKeyspaceSession(); + List boundStatements = insertIntoPosts(session, post); + //boundStatements.forEach(stmt -> writeBatch.add(stmt)); + //an entry in posts + //an entry in the user Timeline + BoundStatement stmt2 = createUserTimelineEntry(session).bind(post.getEntityId(), post.getTime().toInstant(), UUID.fromString(post.getKey())); + boundStatements.add(stmt2); + //an entry in the VRES Timeline iff vreid field is not empty + if (post.getVreid() != null && post.getVreid().compareTo("") != 0){ + BoundStatement stmt3 = createVreTimelineEntry(session).bind(post.getVreid(), post.getTime().toInstant(), UUID.fromString(post.getKey())); + boundStatements.add(stmt3); + } + BatchStatement writeBatch = getBatch().addAll(boundStatements); + + Boolean result = session.execute(writeBatch).wasApplied(); + if (result){ + _log.info("Wrote user post with id " + post.getKey()); + } + + + return result; + } + /** + * @inheritDoc + */ + @Override + public boolean saveUserPost(Post post, List attachments) { + if (attachments != null && !attachments.isEmpty()) + post.setMultiFileUpload(true); + boolean savePostResult = saveUserPost(post); + if (savePostResult) { + _log.info("Post has been saved"); + String postkey = post.getKey(); + for (Attachment attachment : attachments) { + boolean attachSaveResult = saveAttachmentEntry(postkey, attachment); + if (!attachSaveResult) + _log.info("Some of the attachments failed to be saved: " + attachment.getName()); + } + return true; + } + else return false; + } + /** + * @inheritDoc + */ + @Override + public boolean saveAppPost(Post post) { + CqlSession session = conn.getKeyspaceSession(); + + List boundStatements = insertIntoPosts(session, post); + //an entry in the Applications Timeline + BoundStatement stmt2 = createAppTimelineEntry(session).bind(post.getEntityId(), post.getTime().toInstant(), UUID.fromString(post.getKey())); + boundStatements.add(stmt2); + //an entry in the VRES Timeline iff vreid field is not empty + if (post.getVreid() != null && post.getVreid().compareTo("") != 0){ + BoundStatement stmt3 = createVreTimelineEntry(session).bind(post.getVreid(), post.getTime().toInstant(), UUID.fromString(post.getKey())); + boundStatements.add(stmt3); + } + BatchStatement writeBatch = getBatch().addAll(boundStatements); + + boolean result = session.execute(writeBatch).wasApplied(); + if (result) + _log.info("Wrote app post with id " + post.getKey()); + + return result; + } + /** + * @inheritDoc + */ + @Override + public boolean saveAppPost(Post post, List attachments) { + if (attachments != null && !attachments.isEmpty()) + post.setMultiFileUpload(true); + boolean savePostResult = saveAppPost(post); + if (savePostResult) { + String PostKey = post.getKey(); + for (Attachment attachment : attachments) { + boolean attachSaveResult = saveAttachmentEntry(PostKey, attachment); + if (!attachSaveResult) + _log.warn("Some of the attachments failed to be saved: " + attachment.getName()); + } + return true; + } + else return false; + } + /** + * @inheritDoc + */ + @Override + public boolean savePostToVRETimeline(String postKey, String vreid) throws PostIDNotFoundException { + Post toCheck; + try { + toCheck = readPost(postKey); + if (toCheck == null) + throw new PostIDNotFoundException("Could not find Post with id " + postKey, postKey); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch().add(createVreTimelineEntry(session).bind(vreid, toCheck.getTime().toInstant(), UUID.fromString(toCheck.getKey()))); + + try{ + boolean res = session.execute(writeBatch).wasApplied(); + + return res; + }catch (Exception e) { + e.printStackTrace(); + return false; + } + } + /** + * @inheritDoc + */ + @Override + public Post readPost(String postid) + throws PrivacyLevelTypeNotFoundException, + PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException { + CqlSession session = conn.getKeyspaceSession(); + Post post; + try{ + post = findPostById(postid, session).get(); + + } catch (Exception e){ + e.printStackTrace(); + + return null; + } + return post; + } + /** + * @inheritDoc + */ + @Override + public List getRecentPostsByUserAndDate(String userid, long timeInMillis) throws IllegalArgumentException { + Date now = new Date(); + if (timeInMillis > now.getTime()) + throw new IllegalArgumentException("the timeInMillis must be before today"); + + ResultSet result = null; + try { + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_TIMELINE_POSTS).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e) { + e.printStackTrace(); + } + Listrows = result.all(); + List toReturn = new ArrayList<>(); + for (Row row: rows){ + Instant postTime = row.getInstant(TIMESTAMP); + if (Date.from(postTime).getTime() > timeInMillis){ + try{ + Post toCheck = readPost(row.getUuid(POST_ID).toString()); + if (toCheck.getType() != PostType.DISABLED) + toReturn.add(toCheck); + } catch (ColumnNameNotFoundException | PrivacyLevelTypeNotFoundException | PostIDNotFoundException | + PostTypeNotFoundException e) { + throw new RuntimeException(e); + } + } + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public boolean deletePost(String postid) throws PostIDNotFoundException, PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException { + Post toDelete = readPost(postid); + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch().add(updatePostEntry(session, TYPE).bind(PostType.DISABLED.toString(), UUID.fromString(toDelete.getKey()))); + + try { + session.execute(writeBatch); + + } catch (Exception e) { + _log.error("Delete Post ERROR for postid " + postid); + + return false; + } + _log.info("Delete Post OK"); + return true; + } + /** + * @inheritDoc + */ + @Override + public List getAllPostsByUser(String userid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + return getPostsByIds(getUserPostIds(userid)); + } + /** + * @inheritDoc + */ + @Override + public List getAllPostsByApp(String appid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + return getPostsByIds(getAppPostIds(appid)); + } + /** + * @inheritDoc + * @throws Exception + */ + @Override + public List getRecentCommentedPostsByUserAndDate(String userid, + long timeInMillis) throws Exception { + + List toReturn = new ArrayList(); + + Date now = new Date(); + if (timeInMillis > now.getTime()) + throw new IllegalArgumentException("the timeInMillis must be before today"); + + if(userid == null || userid.isEmpty()) + throw new IllegalArgumentException("the userId parameter cannot be null/empty"); + + // get the last comments by the user (it is not needed to get them sorted) + List lastComments = getRecentCommentsByUserAndDateBody(userid, timeInMillis, false); + + // evaluate unique posts' ids + HashSet postIds = new HashSet(); + + for (Comment comment : lastComments) { + String postId = comment.getPostid(); + try{ + if(!postIds.contains(postId)){ + postIds.add(postId); + toReturn.add(readPost(postId)); + } + }catch(Exception e){ + _log.error("Unable to retrieve post with id " + postId, e); + } + } + + Collections.sort(toReturn, Collections.reverseOrder()); + return toReturn; + } + /** + * helper method that retrieve all the posts belongin to a list of Ids + * @param postIds + * @return + * @throws ColumnNameNotFoundException + * @throws PostIDNotFoundException + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + */ + private List getPostsByIds(List postIds) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException { + ArrayList toReturn = new ArrayList(); + for (String postid : postIds) { + Post toAdd = readPost(postid); + if (toAdd.getType() == PostType.TWEET || toAdd.getType() == PostType.SHARE || toAdd.getType() == PostType.PUBLISH) + toReturn.add(toAdd); + } + return toReturn; + } + /** + * helper method that retrieve all the post Ids belonging to a user + * @param userid user identifier + * @return simply return a list of user post UUID in chronological order from the oldest to the more recent + */ + private ArrayList getUserPostIds(String userid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_TIMELINE_POSTS).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + ArrayList toReturn = new ArrayList<>(); + Listrows = result.all(); + for(Row row: rows){ + try { + String postid = row.getUuid(POST_ID).toString(); + toReturn.add(postid); + } catch (RuntimeException e) { + throw new RuntimeException(e); + } + } + return toReturn; + } + + /** + * helper method that return whether the user + * @param userid user identifier + * @param postid the post identifier + * @return true if the post id liked already + */ + private boolean isPostLiked(String userid, String postid) { + + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_LIKED_POSTS).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + + try { + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + Listrows = result.all(); + for (Row row: rows){ + if (row.getUuid(POST_ID).toString().equals(postid)){ + return true; + } + } + + return false; + } + + /** + * helper method that retrieve all the post Ids belonging to an application + * @param appid application identifier + * @return simply return a list of app post UUID in chronological order from the oldest to the more recent + */ + private ArrayList getAppPostIds(String appid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(APP_TIMELINE_POSTS).all() + .whereColumn(APP_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(appid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + Listrows=result.all(); + ArrayList toReturn=new ArrayList<>(); + for(Row row: rows){ + try { + String postid = row.getUuid(POST_ID).toString(); + toReturn.add(postid); + } catch (RuntimeException e) { + throw new RuntimeException(e); + } + } + + + return toReturn; + } + + @Override + public List getAllPortalPrivacyLevelPosts() throws PostTypeNotFoundException, ColumnNameNotFoundException, PrivacyLevelTypeNotFoundException { + //possible error index + ArrayList toReturn = new ArrayList(); + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(POSTS).all() + .whereColumn(PRIVACY).isEqualTo(QueryBuilder.bindMarker()) + .limit(20) + .build()); + try { + result = session.execute(stmtFind.bind(PrivacyLevel.PORTAL.toString())); + + } catch (Exception e){ + e.printStackTrace(); + + } + Listrows=result.all(); + for (Row row: rows) { + Post toAdd = readPostFromRow(row); + if (toAdd.getType() == PostType.TWEET || toAdd.getType() == PostType.SHARE || toAdd.getType() == PostType.PUBLISH) + toReturn.add(toAdd); + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getRecentPostsByUser(String userid, int quantity) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + ArrayList toReturn = new ArrayList(); + ArrayList postIDs = getUserPostIds(userid); + //check if quantity is greater than user posts + quantity = (quantity > postIDs.size()) ? postIDs.size() : quantity; + + //need them in reverse order + for (int i = postIDs.size()-1; i >= (postIDs.size()-quantity); i--) { + Post toAdd = readPost(postIDs.get(i)); + if (toAdd.getType() == PostType.TWEET || toAdd.getType() == PostType.SHARE || toAdd.getType() == PostType.PUBLISH) { + toReturn.add(toAdd); + _log.trace("Read recent post: " + postIDs.get(i)); + } else { + _log.trace("Read and skipped post: " + postIDs.get(i) + " (Removed Post)"); + quantity += 1; //increase the quantity in case of removed post + //check if quantity is greater than user posts + quantity = (quantity > postIDs.size()) ? postIDs.size() : quantity; + } + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getAllPostsByVRE(String vreid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + return getPostsByIds(getVREPostIds(vreid)); + } + @Override + public List getRecentPostsByVRE(String vreid, int quantity) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + ArrayList toReturn = new ArrayList(); + ArrayList postIDs = getVREPostIds(vreid); + //check if quantity is greater than user posts + quantity = (quantity > postIDs.size()) ? postIDs.size() : quantity; + + //need them in reverse order + for (int i = postIDs.size()-1; i >= (postIDs.size()-quantity); i--) { + Post toAdd = readPost(postIDs.get(i)); + if (toAdd.getType() == PostType.TWEET || toAdd.getType() == PostType.SHARE || toAdd.getType() == PostType.PUBLISH) { + toReturn.add(toAdd); + _log.trace("Read recent Post: " + postIDs.get(i)); + } else { + _log.trace("Read and skipped Post: " + postIDs.get(i) + " (Removed Post) ."); + quantity += 1; //increase the quantity in case of removed Post + //check if quantity is greater than user Posts + quantity = (quantity > postIDs.size()) ? postIDs.size() : quantity; + } + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public RangePosts getRecentPostsByVREAndRange(String vreid, int from, int quantity) throws IllegalArgumentException, PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + if (from < 1) { + throw new IllegalArgumentException("From must be greather than 0"); + } + ArrayList postsToReturn = new ArrayList(); + ArrayList postIDs = getVREPostIds(vreid); + + //if from is greater than posts size return empty + if (from >= postIDs.size()) { + _log.warn("The starting point of the range is greather than the total number of posts for this timeline: " + from + " >= " + postIDs.size()); + return new RangePosts(); + } + + int rangeStart = postIDs.size()-from; + int rangeEnd = rangeStart-quantity; + + //check that you reached the end + if (rangeEnd<1) + rangeEnd = 0; + + _log.debug("BEFORE starting Point=" + rangeStart + " rangeEnd= " + rangeEnd); + //need them in reverse order + int howMany = from; + for (int i = rangeStart; i > rangeEnd; i--) { + Post toAdd = readPost(postIDs.get(i)); + if (toAdd.getType() == PostType.TWEET || toAdd.getType() == PostType.SHARE || toAdd.getType() == PostType.PUBLISH) { + postsToReturn.add(toAdd); + _log.trace("Read recent post, i=" + i + " id= " + postIDs.get(i)); + } else { + _log.trace("Read and skipped post, i=" + i + " id=: " + postIDs.get(i) + " (Removed post) ."); + rangeEnd -= 1; //increase the upTo in case of removed post + //check if quantity is greater than user posts + rangeEnd = (rangeEnd > 0) ? rangeEnd : 0; + } + howMany++; + } + _log.debug("AFTER: starting Point==" + rangeStart + " rangeEnd= " + rangeEnd); + return new RangePosts(howMany+1, postsToReturn); + } + /** + * get a list of user vre post UUIDs in chronological order from the oldest to the more recent + * @param vreid vreid identifier (scope) + * @return simply return a list of user vre post UUIDs in chronological order from the oldest to the more recent + */ + private ArrayList getVREPostIds(String vreid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(VRE_TIMELINE_POSTS).all() + .whereColumn(VRE_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(vreid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + Listrows=result.all(); + ArrayList toReturn=new ArrayList<>(); + for(Row row: rows){ + try { + String postid = row.getUuid(POST_ID).toString(); + toReturn.add(postid); + } catch (RuntimeException e) { + throw new RuntimeException(e); + } + } + + + return toReturn; + } + /* + * + ********************** NOTIFICATIONS *********************** + * + */ + private List insertIntoNotifications(CqlSession session, Notification notification){ + PreparedStatement prepStmt1 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(TYPE, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt2 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(USER_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt3 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(SUBJECT_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt4 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(TIMESTAMP, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt5 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(DESCRIPTION, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt6 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(URI, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt7 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(SENDER_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt8 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(SENDER_FULL_NAME, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt9 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(SENDER_THUMBNAIL_URL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt10 = session.prepare(QueryBuilder.insertInto(NOTIFICATIONS).value(NOT_ID, QueryBuilder.bindMarker()).value(IS_READ, QueryBuilder.bindMarker()).build()); + + List boundStatements = new ArrayList<>(); + + if(notification.getType()!=null){ + boundStatements.add(prepStmt1.bind(UUID.fromString(notification.getKey()),notification.getType().toString())); + } + + if(notification.getUserid()!=null){ + boundStatements.add(prepStmt2.bind(UUID.fromString(notification.getKey()), notification.getUserid())); + } + + if(notification.getSubjectid()!=null){ + boundStatements.add(prepStmt3.bind(UUID.fromString(notification.getKey()), notification.getSubjectid())); + } + if(notification.getTime()!=null){ + boundStatements.add(prepStmt4.bind(UUID.fromString(notification.getKey()),notification.getTime().toInstant())); + } + if(notification.getDescription()!=null){ + boundStatements.add(prepStmt5.bind(UUID.fromString(notification.getKey()),notification.getDescription())); + }if(notification.getUri()!=null){ + boundStatements.add(prepStmt6.bind(UUID.fromString(notification.getKey()),notification.getUri())); + }if(notification.getSenderid()!=null){ + boundStatements.add(prepStmt7.bind(UUID.fromString(notification.getKey()),notification.getSenderid())); + }if(notification.getSenderFullName()!=null){ + boundStatements.add(prepStmt8.bind(UUID.fromString(notification.getKey()),notification.getSenderFullName())); + }if(notification.getSenderThumbnail()!=null){ + boundStatements.add(prepStmt9.bind(UUID.fromString(notification.getKey()),notification.getSenderThumbnail())); + } + + boundStatements.add(prepStmt10.bind(UUID.fromString(notification.getKey()),notification.isRead())); + + return boundStatements; + } + + + /** + * @inheritDoc + */ + @Override + public boolean saveNotification(Notification n) { + CqlSession session = conn.getKeyspaceSession(); + + List boundStatements = insertIntoNotifications(session, n); + + //an entry in the user Notifications Timeline + BoundStatement stmt2 = createUserNotificationsEntry(session).bind(n.getUserid(), n.getTime().toInstant(), UUID.fromString(n.getKey())); + boundStatements.add(stmt2); + + // save key in the unread notifications column family too + BoundStatement stmt3 = createUnreadNotificationEntry(session).bind(n.getUserid(), n.getTime().toInstant(), UUID.fromString(n.getKey())); + boundStatements.add(stmt3); + BatchStatement writeBatch = getBatch().addAll(boundStatements); + try{ + boolean res = session.execute(writeBatch).wasApplied(); + + return res; + }catch (Exception e){ + e.printStackTrace(); + return false; + } + } + + /** + * @inheritDoc + */ + @Override + public Notification readNotification(String notificationid) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException { + Notification toReturn = new Notification(); + CqlSession session = conn.getKeyspaceSession(); + try { + toReturn = findNotById(notificationid, session).get(); + + } + catch (Exception e) { + e.printStackTrace(); + + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public boolean setNotificationRead(String notificationidToSet) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException { + Notification toSet = readNotification(notificationidToSet); + if (toSet == null) + throw new NotificationIDNotFoundException("The specified notification to set Read with id: " + notificationidToSet + " does not exist"); + + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch() + + //update the entry in notifications + .add(updateNotificationEntry(session,IS_READ).bind(true,UUID.fromString(notificationidToSet))) + + // delete the notification's key from the unread notifications column family + .add(deleteUnreadNotEntry(session).bind(toSet.getUserid(), toSet.getTime().toInstant())); + + // execute the operations + try { + boolean res = session.execute(writeBatch).wasApplied(); + + return res; + } catch (Exception e) { + _log.error("ERROR while setting Notification " + notificationidToSet + " to read."); + return false; + } + } + /** + * + * @param userid user identifier + * @return simply return a list of user notifications UUID in chronological order from the oldest to the more recent + */ + private ArrayList getUserNotificationsIds(String userid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_NOTIFICATIONS).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + ArrayList toReturn = new ArrayList(); + + // Iterate rows and their columns + Listrows=result.all(); + for (Row row : rows) { + toReturn.add(row.getUuid(NOT_ID).toString()); + } + return toReturn; + } + /** + * Return a list of not read notifications by user userid (messages as well as other notifications) + * @param userid user identifier + * @return simply return a list of not read user notifications UUID in chronological order from the oldest to the more recent + */ + private ArrayList getUnreadUserNotificationsIds(String userid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_NOTIFICATIONS_UNREAD).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + ArrayList toReturn = new ArrayList(); + + // Iterate rows and their columns + Listrows=result.all(); + for (Row row : rows) { + toReturn.add(row.getUuid(NOT_ID).toString()); + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getAllNotificationByUser(String userid, int limit) throws NotificationTypeNotFoundException, ColumnNameNotFoundException { + ArrayList toReturn = new ArrayList(); + ArrayList notificationsIDs = getUserNotificationsIds(userid); + //check if quantity is greater than user posts + limit = (limit > notificationsIDs.size()) ? notificationsIDs.size() : limit; + + //need them in reverse order + for (int i = notificationsIDs.size()-1; i >= (notificationsIDs.size()-limit); i--) { + Notification toAdd = null; + try { + toAdd = readNotification(notificationsIDs.get(i)); + toReturn.add(toAdd); + } catch (NotificationIDNotFoundException e) { + _log.error("Notification not found id=" + notificationsIDs.get(i)); + } + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getUnreadNotificationsByUser(String userid) throws NotificationTypeNotFoundException, ColumnNameNotFoundException, NotificationIDNotFoundException { + ArrayList toReturn = new ArrayList(); + ArrayList notificationsIDs = getUnreadUserNotificationsIds(userid); + + //need them in reverse order + for (int i = notificationsIDs.size()-1; i >= 0; i--) { + try{ + toReturn.add(readNotification(notificationsIDs.get(i))); + }catch(Exception e){ + _log.error("Unable to read notification with key " + notificationsIDs.get(i)); + } + } + + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getRangeNotificationsByUser(String userid,int from, int quantity) throws NotificationTypeNotFoundException, ColumnNameNotFoundException, NotificationIDNotFoundException { + if (from < 1) { + throw new IllegalArgumentException("From must be greather than 0"); + } + ArrayList toReturn = new ArrayList(); + ArrayList notificationsIDs = getUserNotificationsIds(userid); + + //if from is greater than posts size return empty + if (from >= notificationsIDs.size()) { + _log.warn("The starting point of the range is greather than the total number of posts for this timeline: " + from + " >= " + notificationsIDs.size()); + return new ArrayList(); + } + + int rangeStart = notificationsIDs.size()-from; + int rangeEnd = rangeStart-quantity; + + //check that you reached the end + if (rangeEnd<1) + rangeEnd = 0; + + _log.debug("BEFORE starting Point=" + rangeStart + " rangeEnd= " + rangeEnd); + //need them in reverse order + for (int i = rangeStart; i > rangeEnd; i--) { + Notification toAdd = readNotification(notificationsIDs.get(i)); + toReturn.add(toAdd); + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public boolean setAllNotificationReadByUser(String userid) throws NotificationTypeNotFoundException, ColumnNameNotFoundException { + + // get the list of unread notifications + ArrayList notificationsIDs = getUnreadUserNotificationsIds(userid); + + for (int i = notificationsIDs.size()-1; i >= 0; i--) { + try{ + + // set to read (and automatically remove from the unread column family) + setNotificationRead(notificationsIDs.get(i)); + + } catch (NotificationIDNotFoundException e) { + _log.error("Could not set read notification with id =" + notificationsIDs.get(i)); + } + } + return true; + } + + /** + * @inheritDoc + */ + @Override + public boolean checkUnreadNotifications(String userid) throws NotificationTypeNotFoundException, ColumnNameNotFoundException { + + ArrayList unreadNotifications = getUnreadUserNotificationsIds(userid); + + for (int i = unreadNotifications.size() - 1; i >= 0; i--) { + Notification toAdd; + try { + toAdd = readNotification(unreadNotifications.get(i)); + if (toAdd.getType() != NotificationType.MESSAGE) + return true; + } catch (NotificationIDNotFoundException e) { + _log.error("Notification not found with id = " + unreadNotifications.get(i)); + } + } + + return false; + } + /** + * @inheritDoc + */ + @Override + public boolean checkUnreadMessagesNotifications(String userid) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException { + + ArrayList unreadNotifications = getUnreadUserNotificationsIds(userid); + + for (int i = unreadNotifications.size() - 1; i >= 0; i--) { + Notification toAdd; + try { + toAdd = readNotification(unreadNotifications.get(i)); + if (toAdd.getType() == NotificationType.MESSAGE) + return true; + } catch (NotificationIDNotFoundException e) { + _log.error("Notification not found with id = " + unreadNotifications.get(i)); + } + } + + return false; + } + /* + * + ********************** NOTIFICATION SETTINGS *********************** + * + */ + /** + * @inheritDoc + */ + @Override + public List getUserNotificationChannels(String userid, NotificationType notificationType) throws NotificationChannelTypeNotFoundException, NotificationTypeNotFoundException { + _log.info("Asking for Single Notification preference of " + userid + " Type: " + notificationType); + List toReturn = new ArrayList(); + NotificationChannelType[] toProcess = getUserNotificationPreferences(userid).get(notificationType); + _log.info("size of user notification preferences" + toProcess.length); + if (toProcess == null) { + _log.info("Single Notification preference of " + userid + " Type: " + notificationType + " not existing ... creating default"); + return createNewNotificationType(userid, notificationType); + } + else if (toProcess.length == 0) + return toReturn; + else + for (int i = 0; i < toProcess.length; i++) { + toReturn.add(toProcess[i]); + } + return toReturn; + } + /** + * called when you add new notification types where the setting does not exist yet + * please note: by default we set all notifications + */ + private List createNewNotificationType(String userid, NotificationType notificationType) { + List toReturn = new ArrayList(); + _log.info("Create new notification type"); + CqlSession session = conn.getKeyspaceSession(); + + String valueToInsert = ""; + NotificationChannelType[] wpTypes = NotificationChannelType.values(); + + for (int i = 0; i < wpTypes.length; i++) { + valueToInsert += wpTypes[i]; + if (i < wpTypes.length-1) + valueToInsert += ","; + toReturn.add(wpTypes[i]); //add the new added notification type + } + + BatchStatement writeBatch = getBatch().add( + createNotificationPreferenceEntry(session).bind(userid, notificationType.toString(), valueToInsert) + ); + boolean res = false; + try{ + res = session.execute(writeBatch).wasApplied(); + + } catch (Exception e){ + e.printStackTrace(); + + } + + if (res) { + _log.info("Set New Notification Setting for " + userid + " OK"); + _log.info("toreturn:" + toReturn.toString()); + return toReturn; + } + _log.info("empty list"); + return new ArrayList(); //no notification if sth fails + } + /** + * @inheritDoc + */ + @Override + public boolean setUserNotificationPreferences(String userid, Map enabledChannels) { + CqlSession session = conn.getKeyspaceSession(); + List boundStatements = new ArrayList<>(); + + for (NotificationType nType : enabledChannels.keySet()) { + String valueToInsert = ""; + _log.info("Type: " + nType.toString()); + int channelsNo = (enabledChannels.get(nType) != null) ? enabledChannels.get(nType).length : 0; + for (int i = 0; i < channelsNo; i++) { + _log.info(enabledChannels.get(nType)[i].toString()); + valueToInsert += NotificationChannelType.valueOf(enabledChannels.get(nType)[i].toString()); + if (i < channelsNo-1) + valueToInsert += ","; + } + if (channelsNo == 0) { //in case no channels were selected + valueToInsert = ""; + _log.trace("No Channels selected for " + nType + " by " + userid); + } + + boundStatements.add(createNotificationPreferenceEntry(session).bind(userid, nType.toString(), valueToInsert)); + } + + BatchStatement writeBatch = getBatch().addAll(boundStatements); + boolean overAllresult = session.execute(writeBatch).wasApplied(); + if (overAllresult) + _log.info("Set Notification Map for " + userid + " OK"); + else + _log.info("Set Notification Map for " + userid + " FAILED"); + return overAllresult; + } + /** + * @inheritDoc + * + * by default Workspace and Calendar Notifications are set to Portal + */ + @Override + public Map getUserNotificationPreferences(String userid) throws NotificationTypeNotFoundException, NotificationChannelTypeNotFoundException { + _log.trace("Asking for Notification preferences of " + userid); + Map toReturn = new HashMap(); + + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_NOTIFICATIONS_PREFERENCES).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + //if there are no settings for this user create an entry and put all of them at true + List results = result.all(); + if (results.isEmpty()) { + _log.info("Userid " + userid + " settings not found, initiating its preferences..."); + HashMap toCreate = new HashMap(); + + for (int i = 0; i < NotificationType.values().length; i++) { + //TODO: Potential bug in NotificationType for workspace are refactored + //create a map with all notification enabled except for workspace notifications (They start with WP_) it was the only quick way + if (NotificationType.values()[i].toString().startsWith("WP_")) { + NotificationChannelType[] wpTypes = { NotificationChannelType.PORTAL }; + toCreate.put(NotificationType.values()[i], wpTypes); + } + else + toCreate.put(NotificationType.values()[i], NotificationChannelType.values()); + } + setUserNotificationPreferences(userid, toCreate); //commit the map + + return toCreate; + } + else { + _log.trace("Notification preferences Found for " + userid); + for (Row row: results){ + String[] channels = row.getString(PREFERENCE).split(","); + if (channels != null && channels.length == 1 && channels[0].toString().equals("") ) { //it is empty, preference is set to no notification at all + toReturn.put(getNotificationType(row.getString(TYPE)), new NotificationChannelType[0]); + } else { + NotificationChannelType[] toAdd = new NotificationChannelType[channels.length]; + for (int i = 0; i < channels.length; i++) { + if (channels[i].compareTo("") != 0) { + toAdd[i] = (getChannelType(channels[i])); + } + } + toReturn.put(getNotificationType(row.getString(TYPE)), toAdd); + } + } + } + return toReturn; + } + /* + * + ********************** COMMENTS *********************** + * + */ + private List insertIntoComments(CqlSession session, Comment comment){ + PreparedStatement prepStmt1 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(USER_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt2 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(FULL_NAME, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt3 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(THUMBNAIL_URL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt4 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(COMMENT, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt5 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(POST_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt6 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(TIMESTAMP, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt7 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(IS_EDIT, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt8 = session.prepare(QueryBuilder.insertInto(COMMENTS).value(COMMENT_ID, QueryBuilder.bindMarker()).value(LAST_EDIT_TIME, QueryBuilder.bindMarker()).build()); + + List boundStatements = new ArrayList<>(); + + if(comment.getUserid()!=null){ + boundStatements.add(prepStmt1.bind(UUID.fromString(comment.getKey()),comment.getUserid())); + } + + if(comment.getFullName()!=null){ + boundStatements.add(prepStmt2.bind(UUID.fromString(comment.getKey()), comment.getFullName())); + } + + if(comment.getThumbnailURL()!=null){ + boundStatements.add(prepStmt3.bind(UUID.fromString(comment.getKey()), comment.getThumbnailURL())); + } + if(comment.getText()!=null){ + boundStatements.add(prepStmt4.bind(UUID.fromString(comment.getKey()),comment.getText())); + } + if(comment.getPostid()!=null){ + boundStatements.add(prepStmt5.bind(UUID.fromString(comment.getKey()),UUID.fromString(comment.getPostid()))); + }if(comment.getTime()!=null){ + boundStatements.add(prepStmt6.bind(UUID.fromString(comment.getKey()),comment.getTime().toInstant())); + } + boundStatements.add(prepStmt7.bind(UUID.fromString(comment.getKey()),comment.isEdit())); + + if(comment.getLastEditTime()!=null){ + boundStatements.add(prepStmt8.bind(UUID.fromString(comment.getKey()),comment.getLastEditTime().toInstant())); + } + + return boundStatements; + } + + /** + * @inheritDoc + */ + @Override + public boolean addComment(Comment comment) throws PostIDNotFoundException { + Post toComment = null; + if (comment == null) + throw new NullArgumentException("Comment must be not null"); + if (comment.getPostid() == null) + throw new NullArgumentException("Comment post id must be not null"); + + String postid = comment.getPostid(); + try { + toComment = readPost(postid); + if (toComment == null) + throw new PostIDNotFoundException("Could not find Post with id " + postid + " to associate this comment", postid); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + _log.info("Writing comment : {}", comment.toString()); + CqlSession session = conn.getKeyspaceSession(); + + List boundStatements = insertIntoComments(session, comment); + + BatchStatement writeBatch = getBatch().addAll(boundStatements); + + try { + ResultSet res = session.execute(writeBatch); + for (ExecutionInfo ex: res.getExecutionInfos()){ + _log.info("Writing comment result errors: {}", ex.getErrors()); + _log.info("Writing comment result payload: {}", ex.getIncomingPayload()); + } + _log.info("Writing comment result executed?: {}", res.wasApplied()); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + //update the comment count + boolean updateCommentNoResult = updatePostCommentsCount(toComment, true); + return updateCommentNoResult; + } + /** + * @inheritDoc + */ + public Comment readCommentById(String commentId) throws CommentIDNotFoundException { + Comment toReturn = null; + + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(COMMENTS).all() + .whereColumn(COMMENT_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(UUID.fromString(commentId))); + toReturn = readCommentFromRow(result.one()); + if (toReturn==null) { + throw new CommentIDNotFoundException("The requested commentId: " + commentId + " is not existing"); + } + + } catch (Exception e){ + e.printStackTrace(); + } + + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getAllCommentByPost(String postid) { + //possible error index + List toReturn = new ArrayList(); + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare("SELECT * FROM comments WHERE postid=?"); + try { + result = session.execute(stmtFind.bind(UUID.fromString(postid))); + Listrows=result.all(); + for (Row row : rows) { + Comment toAdd = readCommentFromRow(row); + toReturn.add(toAdd); + } + } catch (Exception e){ + e.printStackTrace(); + + } + + return toReturn; + } + + /** + * @inheritDoc + * @throws Exception + */ + @Override + public List getRecentCommentsByUserAndDate(final String userid, + final long timeInMillis) throws Exception { + + final List commentsByUser; + + Date now = new Date(); + if (timeInMillis > now.getTime()) + throw new IllegalArgumentException("the timeInMillis must be before today"); + + if(userid == null || userid.isEmpty()) + throw new IllegalArgumentException("the userId parameter cannot be null/empty"); + + + commentsByUser = getRecentCommentsByUserAndDateBody(userid, timeInMillis, true); + + return commentsByUser; } + + /** + * Private method that allows also to specify if the returned list must be sorted or not + * @param userid the user id + * @param timeInMillis the initial time to consider + * @param sort a boolean value to specify if the returned list must be sorted (from the most recent to the oldest comment) + * @return a list of comments recently made by the user + */ + private List getRecentCommentsByUserAndDateBody(final String userid, + final long timeInMillis, boolean sort){ + + //possible error + final List commentsByUser = new ArrayList(); + + CqlSession session = conn.getKeyspaceSession(); + + ResultSet result = null; + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(COMMENTS) + .all() + .build()); + try { + result = session.execute(stmtFind.bind()); + + List results = result.all(); + if (!results.isEmpty()){ + results.parallelStream().forEach( row->{ + if(row.getString(USER_ID).equals(userid)){ + try{ + Comment c = readCommentById(row.getUuid(COMMENT_ID).toString()); + Post p = readPost(c.getPostid()); + if(c.getTime().getTime() >= timeInMillis && + (p.getType() == PostType.TWEET || p.getType() == PostType.SHARE || p.getType() == PostType.PUBLISH)) + commentsByUser.add(c); + }catch(Exception e){ + _log.error("Unable to read comment with id" + row.getString(COMMENT_ID), e); + } + } + } + ); + } + } catch (Exception e){ + e.printStackTrace(); + + } + + if(sort) + Collections.sort(commentsByUser, Collections.reverseOrder()); + return commentsByUser; + } + + /** + * @inheritDoc + */ + @Override + public boolean editComment(Comment comment2Edit) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, CommentIDNotFoundException, PostIDNotFoundException { + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch().add(updateCommentEntry(session, COMMENT).bind(comment2Edit.getText(), UUID.fromString(comment2Edit.getKey()))) + .add(updateCommentEntry(session, IS_EDIT).bind(true, UUID.fromString(comment2Edit.getKey()))) + .add(updateCommentEntry(session, LAST_EDIT_TIME).bind(new Date().toInstant(), UUID.fromString(comment2Edit.getKey()))); + + try { + boolean res = session.execute(writeBatch).wasApplied(); + + _log.info("Comments update OK to: " + comment2Edit.getText()); + return res; + } catch (Exception e) { + + _log.error("Comments update NOT OK "); + return false; + } + } + + /** + * @inheritDoc + */ + @Override + public boolean deleteComment(String commentid, String Postid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, CommentIDNotFoundException, PostIDNotFoundException { + Post toUpdate = readPost(Postid); + boolean updateCommentNoResult = false; + + updateCommentNoResult = updatePostCommentsCount(toUpdate, false); + if (updateCommentNoResult) { + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch().add(deleteCommentEntry(session).bind(UUID.fromString(commentid))); + + try { + session.execute(writeBatch); + + } catch (Exception e) { + + _log.error("Comment Delete FAILED for " + commentid + " from Post " + Postid); + e.printStackTrace(); + } + _log.trace("Comment Deleted " + commentid + " from Post " + Postid); + } + + + return updateCommentNoResult; + } + + private List insertIntoLikes(CqlSession session, Like like){ + + PreparedStatement prepStmt1 = session.prepare(QueryBuilder.insertInto(LIKES).value(LIKE_ID, QueryBuilder.bindMarker()).value(USER_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt2 = session.prepare(QueryBuilder.insertInto(LIKES).value(LIKE_ID, QueryBuilder.bindMarker()).value(FULL_NAME, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt3 = session.prepare(QueryBuilder.insertInto(LIKES).value(LIKE_ID, QueryBuilder.bindMarker()).value(THUMBNAIL_URL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt4 = session.prepare(QueryBuilder.insertInto(LIKES).value(LIKE_ID, QueryBuilder.bindMarker()).value(POST_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt5 = session.prepare(QueryBuilder.insertInto(LIKES).value(LIKE_ID, QueryBuilder.bindMarker()).value(TIMESTAMP, QueryBuilder.bindMarker()).build()); + + List boundStatements = new ArrayList<>(); + + if(like.getUserid()!=null){ + boundStatements.add(prepStmt1.bind(UUID.fromString(like.getKey()),like.getUserid())); + } + + if(like.getFullName()!=null){ + boundStatements.add(prepStmt2.bind(UUID.fromString(like.getKey()), like.getFullName())); + } + + if(like.getThumbnailURL()!=null){ + boundStatements.add(prepStmt3.bind(UUID.fromString(like.getKey()), like.getThumbnailURL())); + } + + if(like.getPostid()!=null){ + boundStatements.add(prepStmt4.bind(UUID.fromString(like.getKey()),UUID.fromString(like.getPostid()))); + }if(like.getTime()!=null){ + boundStatements.add(prepStmt5.bind(UUID.fromString(like.getKey()),like.getTime().toInstant())); + } + + return boundStatements; + } + + /** + * @inheritDoc + */ + @Override + public boolean like(Like like) throws PostIDNotFoundException { + Post toLike = null; + if (like == null) + throw new NullArgumentException("Like must be not null"); + if (like.getPostid() == null) + throw new NullArgumentException("Like post id must be not null"); + + String postId = like.getPostid(); + try { + toLike = readPost(postId); + if (toLike == null) + throw new PostIDNotFoundException("Could not find post with id " + postId + " to associate this like", postId); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + if (isPostLiked(like.getUserid(), postId)) { + _log.info("User " + like.getUserid() + " already liked post " + postId); + return true; + } + else { + CqlSession session = conn.getKeyspaceSession(); + // Inserting data + //an entry in the post CF + //and an entry in the UserLikesCF + List boundStatements = insertIntoLikes(session,like); + BoundStatement stmt2 = createNewUserLikesEntry(session).bind(like.getUserid(), UUID.fromString(like.getKey()), UUID.fromString(like.getPostid())); + boundStatements.add(stmt2); + + //boundStatements.forEach(stmt->writeBatch.add(stmt)); + BatchStatement writeBatch = getBatch().addAll(boundStatements); + + try { + session.execute(writeBatch); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return updatePostLikesCount(toLike, true); + } + } + /** + * @inheritDoc + */ + @Override + public boolean unlike(String userid, String likeid, String postId) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, LikeIDNotFoundException, PostIDNotFoundException { + Post toUpdate = readPost(postId); + boolean updateLikeNoResult = false; + + updateLikeNoResult = updatePostLikesCount(toUpdate, false); //this remove 1 from the post CF LikeNO + if (updateLikeNoResult) { + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch().add(deleteLikeEntry(session).bind(UUID.fromString(likeid))) + .add(deleteUserLikeEntry(session).bind(userid, UUID.fromString(likeid))); + try { + session.execute(writeBatch); + + } catch (Exception e) { + + _log.error("Like Delete FAILED for " + likeid + " from post " + postId); + e.printStackTrace(); + } + _log.trace("Unlike ok for " + likeid + " from post " + postId); + } + return updateLikeNoResult; + + } + /** + * @inheritDoc + */ + @Override + public List getAllLikedPostIdsByUser(String userid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(USER_LIKED_POSTS).all() + .whereColumn(USER_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(userid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + ArrayList toReturn = new ArrayList(); + for (Row row: result){ + toReturn.add(row.getUuid(POST_ID).toString()); + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getAllLikedPostsByUser(String userid, int limit) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException { + ArrayList toReturn = new ArrayList(); + List likedPostIDs = getAllLikedPostIdsByUser(userid); + + //check if quantity is greater than user posts + limit = (limit > likedPostIDs.size()) ? likedPostIDs.size() : limit; + + //need them in reverse order + for (int i = likedPostIDs.size()-1; i >= (likedPostIDs.size()-limit); i--) { + Post toAdd = readPost(likedPostIDs.get(i)); + if (toAdd.getType() == PostType.TWEET || toAdd.getType() == PostType.SHARE || toAdd.getType() == PostType.PUBLISH) { + toReturn.add(toAdd); + _log.trace("Read recent post: " + likedPostIDs.get(i)); + } else { + _log.trace("Read and skipped post: " + likedPostIDs.get(i) + " (Removed post)"); + limit += 1; //increase the quantity in case of removed post + //check if quantity is greater than user posts + limit = (limit > likedPostIDs.size()) ? likedPostIDs.size() : limit; + } + } + return toReturn; + } + + /** + * @inheritDoc + */ + @Override + public List getRecentLikedPostsByUserAndDate(String userid, + long timeInMillis) throws IllegalArgumentException { + + List toReturn = new ArrayList<>(); + + Date now = new Date(); + if (timeInMillis > now.getTime()) + throw new IllegalArgumentException("the timeInMillis must be before today"); + + if(userid == null || userid.isEmpty()) + throw new IllegalArgumentException("the userId parameter cannot be null/empty"); + + // get the list of liked posts + List likedPostsIdsByUser = getAllLikedPostIdsByUser(userid); + + if(likedPostsIdsByUser != null && !likedPostsIdsByUser.isEmpty()){ + for(int i = likedPostsIdsByUser.size() - 1; i >= 0; i--){ + String postid = likedPostsIdsByUser.get(i); + try{ + + // retrieve the Post + Post toCheck = readPost(postid); + boolean isPostOk = (toCheck.getType() == PostType.TWEET || toCheck.getType() == PostType.SHARE || toCheck.getType() == PostType.PUBLISH); + + // retrieve the like of the user for the post + if(isPostOk){ + List likes = getAllLikesByPost(postid); + for (Like like : likes) { + if(like.getTime().getTime() >= timeInMillis && like.getUserid().equals(userid)) + toReturn.add(toCheck); + } + } + + }catch(Exception e){ + _log.error("Skipped post with id " + postid, e); + } + } + } + + // please check consider that if a user made like recently to an old post, well it could happen that this + // post comes first than a newer post in the toReturn list. Thus we need to sort it. + Collections.sort(toReturn, Collections.reverseOrder()); + + return toReturn; + } + + /** + * @inheritDoc + */ + @Override + public List getAllLikesByPost(String postid) { + //possible error index + + List toReturn = new ArrayList(); + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(LIKES).all() + .whereColumn(POST_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(UUID.fromString(postid))); + for (Row row: result) { + Like toAdd = readLikeFromRow(row); + toReturn.add(toAdd); + } + + } catch (Exception e){ + e.printStackTrace(); + + } + return toReturn; + } + /* + * + ********************** HASHTAGS *********************** + * + */ + /** + * @inheritDoc + */ + @Override + public boolean saveHashTags(String postId, String vreid, List hashtags) throws PostIDNotFoundException { + Set noduplicatesHashtags = null; + if (hashtags != null && !hashtags.isEmpty()) { + noduplicatesHashtags = new HashSet(hashtags); + } + // Inserting data + CqlSession session = conn.getKeyspaceSession(); + for (String hashtag : noduplicatesHashtags) { + String lowerCaseHashtag = hashtag.toLowerCase(); + boolean firstInsert = session.execute((createNewHashtagTimelineEntry(session).bind(lowerCaseHashtag, UUID.fromString(postId), vreid))).wasApplied(); + boolean secondInsert = updateVREHashtagCount(vreid, lowerCaseHashtag, true); + if (! (firstInsert && secondInsert)) { + _log.error("saveHashTags: Could not save the hashtag(s)"); + + return false; + } + } + + return true; + } + /** + * @inheritDoc + */ + @Override + public boolean deleteHashTags(String postId, String vreid, List hashtags) throws PostIDNotFoundException { + Set noduplicatesHashtags = null; + if (hashtags != null && !hashtags.isEmpty()) { + noduplicatesHashtags = new HashSet(hashtags); + } + // Inserting data + CqlSession session = conn.getKeyspaceSession(); + for (String hashtag : noduplicatesHashtags) { + String lowerCaseHashtag = hashtag.toLowerCase(); + boolean firstDelete = session.execute(deleteHashtagEntry(session).bind(lowerCaseHashtag, UUID.fromString(postId))).wasApplied(); + boolean secondInsert = updateVREHashtagCount(vreid, lowerCaseHashtag, false); + if (! (firstDelete && secondInsert)) { + _log.error("deleteHashTags: Could not delete the hashtag(s)"); + + return false; + + } + } + + return true; + } + /** + * @inheritDoc + */ + @Override + public boolean saveHashTagsComment(String commentId, String vreid, List hashtags) throws CommentIDNotFoundException { + Set noduplicatesHashtags = null; + if (hashtags != null && !hashtags.isEmpty()) { + noduplicatesHashtags = new HashSet(hashtags); + } + // Inserting datacommentIdcommentId + CqlSession session = conn.getKeyspaceSession(); + for (String hashtag : noduplicatesHashtags) { + String lowerCaseHashtag = hashtag.toLowerCase(); + boolean firstInsert = session.execute(createNewHashtagCommentEntry(session).bind(hashtag, UUID.fromString(commentId), vreid)).wasApplied(); + boolean secondInsert = false; + if(firstInsert) + secondInsert = updateVREHashtagCount(vreid, lowerCaseHashtag, true); + if (! (firstInsert && secondInsert)) { + _log.error("saveHashTags: Could not save the hashtag(s)"); + + return false; + } + } + + return true; + } + /** + * @inheritDoc + */ + @Override + public boolean deleteHashTagsComment(String commentId, String vreid, List hashtags) throws CommentIDNotFoundException { + Set noduplicatesHashtags = null; + if (hashtags != null && !hashtags.isEmpty()) { + noduplicatesHashtags = new HashSet(hashtags); + } + // Inserting data + CqlSession session = conn.getKeyspaceSession(); + for (String hashtag : noduplicatesHashtags) { + String lowerCaseHashtag = hashtag.toLowerCase(); + boolean firstDelete = session.execute(deleteHashtagCommentEntry(session).bind(lowerCaseHashtag, UUID.fromString(commentId))).wasApplied(); + if(firstDelete){ + boolean secondInsert = updateVREHashtagCount(vreid, lowerCaseHashtag, false); + if (!(firstDelete && secondInsert)) { + _log.error("deleteHashTags: Could not delete the hashtag(s)"); + + return false; + } + }else{ + _log.error("deleteHashTags: Could not delete the hashtag(s)"); + + return false; + } + } + + return true; + } + /** + * @inheritDoc + */ + @Override + public Map getVREHashtagsWithOccurrence(String vreid) { + ResultSet result = null; + CqlSession session = conn.getKeyspaceSession(); + + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(HASHTAGS_COUNTER).all() + .whereColumn(VRE_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(vreid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + + HashMap toReturn = new HashMap (); + + List rows = result.all(); + // Iterate rows and their columns + for (Row row : rows) { + Integer curValue = (int) row.getLong(COUNT); + if (curValue > 0) + toReturn.put(row.getString(HASHTAG), curValue); + } + + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public Map getVREHashtagsWithOccurrenceFilteredByTime(String vreid, long timestamp){ + CqlSession session = conn.getKeyspaceSession(); + ResultSet result = null; + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(HASHTAGS_COUNTER).all() + .whereColumn(VRE_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(vreid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + HashMap toReturn = new HashMap (); + List rows = result.all(); + // Iterate rows and their columns + for (Row row : rows) { + // retrieve the posts list for this hashtag + List posts = null; + try{ + posts = getVREPostsByHashtag(vreid, row.getString(HASHTAG)); + }catch(Exception e){ + _log.error("Unable to retrieve the list of posts for hashtag" + row.getString(HASHTAG) + " in vre " + vreid); + continue; + } + + if(posts.isEmpty()){ + + _log.info("There are no posts containing hashtag " + row.getString(HASHTAG) + " in vre " + vreid); + continue; + + } + + // retrieve the most recent one among these posts + Collections.sort(posts, Collections.reverseOrder()); + + if(posts.get(0).getTime().getTime() < timestamp){ + continue; + } + + // else.. + int curValue = (int) row.getLong(COUNT); + + if (curValue > 0) + toReturn.put(row.getString(HASHTAG), curValue); + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public List getVREPostsByHashtag(String vreid, String hashtag) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException { + List toReturn = new ArrayList<>(); + CqlSession session = conn.getKeyspaceSession(); + + ResultSet resultPost = null; + PreparedStatement stmtFind1 = session.prepare(QueryBuilder + .selectFrom(HASHTAGGED_POSTS).all() + .whereColumn(HASHTAG) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + resultPost = session.execute(stmtFind1.bind(hashtag)); + } catch (Exception e){ + e.printStackTrace(); + + } + + PreparedStatement stmtFind2 = session.prepare(QueryBuilder + .selectFrom(HASHTAGGED_COMMENTS).all() + .whereColumn(HASHTAG) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + ResultSet resultComment = null; + try { + resultComment = session.execute(stmtFind2.bind(hashtag)); + } catch (Exception e){ + e.printStackTrace(); + + } + + Set postIds = new HashSet<>(); + // Iterate rows and their columns (post) + ListrowsPost=resultPost.all(); + for (Row row : rowsPost) { + if (row.getString(VRE_ID).compareTo(vreid)==0) + postIds.add(row.getUuid(POST_ID).toString()); + } + // Iterate rows and their columns (comments) + ListrowsComment=resultComment.all(); + for (Row row : rowsComment) { + if (row.getString(VRE_ID).compareTo(vreid)==0){ + try { + Comment c = readCommentById(row.getUuid(COMMENT_ID).toString()); + postIds.add(c.getPostid()); + } catch (CommentIDNotFoundException e) { + _log.warn("Failed to fetch comment with id " + row.getString(COMMENT_ID)); + } + } + } + toReturn = getPostsByIds(new ArrayList<>(postIds)); + return toReturn; + } + /* + * + ********************** Invites *********************** + * + */ + private List insertIntoInvites(CqlSession session, Invite invite){ + + PreparedStatement prepStmt1 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(SENDER_USER_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt2 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(VRE_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt3 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(EMAIL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt4 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(CONTROL_CODE, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt5 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(STATUS, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt6 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(TIMESTAMP, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt7 = session.prepare(QueryBuilder.insertInto(INVITES).value(INVITE_ID, QueryBuilder.bindMarker()).value(SENDER_FULL_NAME, QueryBuilder.bindMarker()).build()); + + List boundStatements = new ArrayList<>(); + + if(invite.getSenderUserId()!=null){ + boundStatements.add(prepStmt1.bind(UUID.fromString(invite.getKey()),invite.getSenderUserId())); + } + + if(invite.getVreid()!=null){ + boundStatements.add(prepStmt2.bind(UUID.fromString(invite.getKey()), invite.getVreid())); + } + + if(invite.getInvitedEmail()!=null){ + boundStatements.add(prepStmt3.bind(UUID.fromString(invite.getKey()), invite.getInvitedEmail())); + } + + if(invite.getControlCode()!=null){ + boundStatements.add(prepStmt4.bind(UUID.fromString(invite.getKey()),invite.getControlCode())); + } + if(invite.getStatus()!=null){ + boundStatements.add(prepStmt5.bind(UUID.fromString(invite.getKey()),invite.getStatus().toString())); + } + if(invite.getTime()!=null){ + boundStatements.add(prepStmt6.bind(UUID.fromString(invite.getKey()),invite.getTime().toInstant())); + } + + if(invite.getSenderFullName()!=null){ + boundStatements.add(prepStmt7.bind(UUID.fromString(invite.getKey()),invite.getSenderFullName())); + } + + return boundStatements; + } + + /** + * common part to save a invite + * @param invite + * @return the partial mutation batch instance + */ + private List initSaveInvite(Invite invite, CqlSession session) { + List boundStatements = insertIntoInvites(session, invite); + if (invite == null) + throw new NullArgumentException("Invite instance is null"); + + return boundStatements; + } + + /** + * @inheritDoc + */ + @Override + public String isExistingInvite(String vreid, String email) { + CqlSession session = conn.getKeyspaceSession(); + + ResultSet result = null; + PreparedStatement stmtFind1 = session.prepare(QueryBuilder + .selectFrom(EMAIL_INVITES).all() + .whereColumn(EMAIL) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind1.bind(email)); + } catch (Exception e){ + e.printStackTrace(); + + } + + // Iterate rows and their columns + Listrows=result.all(); + for (Row row : rows) { + if (row.getString(VRE_ID).compareTo(vreid)==0) + return row.getUuid(INVITE_ID).toString(); + + } + return null; + } + /** + * @inheritDoc + */ + @Override + public InviteOperationResult saveInvite(Invite invite) throws AddressException { + if (invite == null) + throw new NullArgumentException("Invite instance is null"); + String email = invite.getInvitedEmail(); + if (! verifyEmail(email)) + throw new AddressException("Email is not valid ->" + email); + if (invite.getVreid() == null || invite.getVreid().equals("")) + throw new NullArgumentException("VREId is null or empty"); + _log.debug("isExistingInvite? " + invite.getInvitedEmail() + " in " + invite.getVreid()); + if (isExistingInvite(invite.getVreid(), invite.getInvitedEmail()) != null) + return InviteOperationResult.ALREADY_INVITED; + _log.debug("Invite not found, proceed to save it ..."); + CqlSession session = conn.getKeyspaceSession(); + List boundStatements = initSaveInvite(invite, session); + //an entry in the VRE Invites + boundStatements.add(createNewVreInviteEntry(session).bind(invite.getVreid(), UUID.fromString(invite.getKey()), InviteStatus.PENDING.toString())); + //an entry in the EMAIL Invites + boundStatements.add(createNewEmailInviteEntry(session).bind(email, invite.getVreid(), UUID.fromString(invite.getKey()))); + BatchStatement writeBatch = getBatch().addAll(boundStatements); + boolean result = session.execute(writeBatch).wasApplied(); + + return result ? InviteOperationResult.SUCCESS : InviteOperationResult.FAILED; + } + /** + * @inheritDoc + */ + @Override + public Invite readInvite(String inviteid) throws InviteIDNotFoundException, InviteStatusNotFoundException { + Invite toReturn = null; + CqlSession session = conn.getKeyspaceSession(); + ResultSet result = null; + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(INVITES).all() + .whereColumn(INVITE_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(UUID.fromString(inviteid))); + toReturn = readAInviteFromRow(result.one()); + if (toReturn == null) { + throw new InviteStatusNotFoundException("The requested inviteid: " + inviteid + " is not existing"); + } + //toReturn = readAInviteFromRow(result.all().get(0)); + + } catch (Exception e){ + e.printStackTrace(); + + } + return toReturn; + } + + /** + * helper method that retrieve all the Invites belonging to a list of Ids + * @param inviteIds the lisf of invites UUID + * @return all the invites belonging to a list of Ids + * @throws InviteIDNotFoundException + * @throws InviteStatusNotFoundException + */ + private List getInvitesById(List inviteIds) throws InviteIDNotFoundException, InviteStatusNotFoundException { + ArrayList toReturn = new ArrayList(); + for (String inviteid : inviteIds) + toReturn.add(readInvite(inviteid)); + + return toReturn; + } + /** + * @inheritDoc + * @throws InviteStatusNotFoundException + */ + @Override + public boolean setInviteStatus(String vreid, String email, InviteStatus status) throws InviteIDNotFoundException, InviteStatusNotFoundException { + String inviteid = isExistingInvite(vreid, email); + Invite toSet = readInvite(inviteid); + if (toSet == null) + throw new InviteIDNotFoundException("The specified invite to set with id: " + inviteid + " does not exist"); + + CqlSession session = conn.getKeyspaceSession(); + BatchStatement writeBatch = getBatch().add(updateInviteEntry(session, STATUS).bind(status.toString(), UUID.fromString(inviteid))) + .add(updateVreInviteEntry(session, STATUS).bind(status.toString(), vreid, UUID.fromString(inviteid))); + try { + session.execute(writeBatch); + + } catch (Exception e) { + _log.error("ERROR while setting Invite " + inviteid + " to " + status.toString()); + + return false; + } + _log.trace("Invite Status Set to " + status.toString() + " OK"); + return true; + } + /** + * @inheritDoc + */ + @Override + public List getInvitedEmailsByVRE(String vreid, InviteStatus... status) throws InviteIDNotFoundException, InviteStatusNotFoundException{ + CqlSession session = conn.getKeyspaceSession(); + ResultSet result = null; + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(VRE_INVITES).all() + .whereColumn(VRE_ID) + .isEqualTo(QueryBuilder.bindMarker()) + .build()); + try { + result = session.execute(stmtFind.bind(vreid)); + + } catch (Exception e){ + e.printStackTrace(); + + } + + ArrayList invitesIds = new ArrayList(); + // Iterate rows and their columns + Listrows=result.all(); + for (Row row : rows) { + if (status != null) { + for (int i = 0; i < status.length; i++) { + if (row.getString(STATUS).compareTo(status[i].toString())==0) + invitesIds.add(row.getUuid(INVITE_ID).toString()); + } + } + else { + invitesIds.add(row.getUuid(INVITE_ID).toString()); + } + } + return getInvitesById(invitesIds); + } + /** + * @inheritDoc + */ + @Override + public List getAttachmentsByPostId(String postId) throws PostIDNotFoundException { + //index error + Post toCheck = null; + try { + toCheck = readPost(postId); + if (toCheck == null) + throw new PostIDNotFoundException("Could not find post with id " + postId, postId); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + List toReturn = new ArrayList(); + CqlSession session = conn.getKeyspaceSession(); + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(ATTACHMENTS).all() + .whereColumn(POST_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()); + ResultSet result = null; + try { + result = session.execute(stmtFind.bind(UUID.fromString(postId))); + // Iterate rows and their columns + Listrows=result.all(); + for (Row row : rows) { + _log.trace("Reading attachment if post=" + row.getUuid(POST_ID).toString()); + Attachment toAdd = readAttachmentFromRow(row); + toReturn.add(toAdd); + } + + } catch (Exception e) { + e.printStackTrace(); + + return null; + } + return toReturn; + } + /** + * @inheritDoc + */ + @Override + public void closeConnection() { + conn.closeConnection(); + } + /* + * + ********************** Helper methods *********************** + * + */ + private List insertIntoAttachments(CqlSession session, Attachment attachment, String postId){ + + + PreparedStatement prepStmt1 = session.prepare(QueryBuilder.insertInto(ATTACHMENTS).value(ATTACH_ID, QueryBuilder.bindMarker()).value(POST_ID, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt2 = session.prepare(QueryBuilder.insertInto(ATTACHMENTS).value(ATTACH_ID, QueryBuilder.bindMarker()).value(URI, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt3 = session.prepare(QueryBuilder.insertInto(ATTACHMENTS).value(ATTACH_ID, QueryBuilder.bindMarker()).value(NAME, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt4 = session.prepare(QueryBuilder.insertInto(ATTACHMENTS).value(ATTACH_ID, QueryBuilder.bindMarker()).value(DESCRIPTION, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt5 = session.prepare(QueryBuilder.insertInto(ATTACHMENTS).value(ATTACH_ID, QueryBuilder.bindMarker()).value(URI_THUMBNAIL, QueryBuilder.bindMarker()).build()); + PreparedStatement prepStmt6 = session.prepare(QueryBuilder.insertInto(ATTACHMENTS).value(ATTACH_ID, QueryBuilder.bindMarker()).value(MIME_TYPE, QueryBuilder.bindMarker()).build()); + + List boundStatements = new ArrayList<>(); + + boundStatements.add(prepStmt1.bind(UUID.fromString(attachment.getId()),UUID.fromString(postId))); + + + if(attachment.getUri()!=null){ + boundStatements.add(prepStmt2.bind(UUID.fromString(attachment.getId()), attachment.getUri())); + } + + if(attachment.getName()!=null){ + boundStatements.add(prepStmt3.bind(UUID.fromString(attachment.getId()), attachment.getName())); + } + + if(attachment.getDescription()!=null){ + boundStatements.add(prepStmt4.bind(UUID.fromString(attachment.getId()),attachment.getDescription())); + } + if(attachment.getThumbnailURL()!=null){ + boundStatements.add(prepStmt5.bind(UUID.fromString(attachment.getId()),attachment.getThumbnailURL())); + } + if(attachment.getMimeType()!=null){ + boundStatements.add(prepStmt6.bind(UUID.fromString(attachment.getId()),attachment.getMimeType())); + } + + + return boundStatements; + } + /** + * @inheritDoc + */ + @Override + public boolean saveAttachmentEntry(String postId, Attachment toSave) { + // Inserting data + CqlSession session = conn.getKeyspaceSession(); + //an entry in the Attachment CF + try { + List boundStatements = insertIntoAttachments(session, toSave, postId); + BatchStatement writeBatch = getBatch().addAll(boundStatements); + ResultSet res = session.execute(writeBatch); + _log.info(res.getExecutionInfos().toString()); + _log.info(""+res.wasApplied()); + + } catch (Exception e) { + + e.printStackTrace(); + return false; + } + return true; + } + + + /** + * simply return an enum representing the privacy level + * @return correct enum representing the privacy level + * @throws NotificationChannelTypeNotFoundException + * @throws PostTypeNotFoundException + */ + private NotificationChannelType getChannelType(String channelName) throws NotificationChannelTypeNotFoundException { + if (channelName.compareTo("PORTAL") == 0) + return NotificationChannelType.PORTAL; + else if (channelName.compareTo("EMAIL") == 0) + return NotificationChannelType.EMAIL; + else if (channelName.compareTo("TWITTER") == 0) + return NotificationChannelType.TWITTER; + else + throw new NotificationChannelTypeNotFoundException("The Notification Channel Type was not recognized should be one of " + NotificationChannelType.values() + " asked for: " + channelName); + } + + /** + * simply return an enum representing the privacy level + * @param privacyLevel . + * @return correct enum representing the privacy level + * @throws PostTypeNotFoundException + */ + private static PrivacyLevel getPrivacyLevel(String privacyLevel) throws PrivacyLevelTypeNotFoundException { + if (privacyLevel.compareTo("CONNECTION") == 0) + return PrivacyLevel.CONNECTION; + else if (privacyLevel.compareTo("PRIVATE") == 0) + return PrivacyLevel.PRIVATE; + else if (privacyLevel.compareTo("PUBLIC") == 0) + return PrivacyLevel.PUBLIC; + else if (privacyLevel.compareTo("VRES") == 0) + return PrivacyLevel.VRES; + else if (privacyLevel.compareTo("SINGLE_VRE") == 0) + return PrivacyLevel.SINGLE_VRE; + else if (privacyLevel.compareTo("PORTAL") == 0) + return PrivacyLevel.PORTAL; + else + throw new PrivacyLevelTypeNotFoundException("The Privacy Level was not recognized should be one of " + PrivacyLevel.values() + " asked for: " + privacyLevel); + } + /** + * simply return an enum representing the post type + * @param type . + * @return correct enum representing the post type + * @throws PostTypeNotFoundException . + */ + private static PostType getPostType(String type) throws PostTypeNotFoundException { + if (type.compareTo("TWEET") == 0) { + return PostType.TWEET; + } + else if (type.compareTo("JOIN") == 0) { + return PostType.JOIN; + } + else if (type.compareTo("PUBLISH") == 0) { + return PostType.PUBLISH; + } + else if (type.compareTo("SHARE") == 0) { + return PostType.SHARE; + } + else if (type.compareTo("ACCOUNTING") == 0) { + return PostType.ACCOUNTING; + } + else if (type.compareTo("DISABLED") == 0) { + return PostType.DISABLED; + } + else + throw new PostTypeNotFoundException("The post Type was not recognized should be one of " + PostType.values() + " asked for: " + type); + } + + /** + * simply return an enum representing the invite status type + * @param type . + * @return correct enum representing the post type + * @throws InviteStatusNotFoundException . + */ + private static InviteStatus getInviteStatusType(String type) throws InviteStatusNotFoundException { + switch (type) { + case "PENDING": + return InviteStatus.PENDING; + case "ACCEPTED": + return InviteStatus.ACCEPTED; + case "REJECTED": + return InviteStatus.REJECTED; + case "RETRACTED": + return InviteStatus.RETRACTED; + default: + throw new InviteStatusNotFoundException("The Invite Status was not recognized should be one of " + InviteStatus.values() + " asked for: " + type); + } + + } + + /** + * simply return an enum representing the post type + * @param type . + * @return correct enum representing the post type + * @throws NotificationTypeNotFoundException . + */ + private static NotificationType getNotificationType(String type) throws NotificationTypeNotFoundException { + if (type.compareTo("WP_FOLDER_SHARE") == 0) { + return NotificationType.WP_FOLDER_SHARE; + } + else if (type.compareTo("WP_FOLDER_UNSHARE") == 0) { + return NotificationType.WP_FOLDER_UNSHARE; + } + else if (type.compareTo("WP_ADMIN_UPGRADE") == 0) { + return NotificationType.WP_ADMIN_UPGRADE; + } + else if (type.compareTo("WP_ADMIN_DOWNGRADE") == 0) { + return NotificationType.WP_ADMIN_DOWNGRADE; + } + else if (type.compareTo("WP_FOLDER_RENAMED") == 0) { + return NotificationType.WP_FOLDER_RENAMED; + } + else if (type.compareTo("WP_FOLDER_ADDEDUSER") == 0) { + return NotificationType.WP_FOLDER_ADDEDUSER; + } + else if (type.compareTo("WP_FOLDER_REMOVEDUSER") == 0) { + return NotificationType.WP_FOLDER_REMOVEDUSER; + } + else if (type.compareTo("WP_ITEM_DELETE") == 0) { + return NotificationType.WP_ITEM_DELETE; + } + else if (type.compareTo("WP_ITEM_UPDATED") == 0) { + return NotificationType.WP_ITEM_UPDATED; + } + else if (type.compareTo("WP_ITEM_NEW") == 0) { + return NotificationType.WP_ITEM_NEW; + } + else if (type.compareTo("WP_ITEM_RENAMED") == 0) { + return NotificationType.WP_ITEM_RENAMED; + } + else if (type.compareTo("OWN_COMMENT") == 0) { + return NotificationType.OWN_COMMENT; + } + else if (type.compareTo("COMMENT") == 0) { + return NotificationType.COMMENT; + } + else if (type.compareTo("MENTION") == 0) { + return NotificationType.MENTION; + } + else if (type.compareTo("LIKE") == 0) { + return NotificationType.LIKE; + } + else if (type.compareTo("CALENDAR_ADDED_EVENT") == 0) { + return NotificationType.CALENDAR_ADDED_EVENT; + } + else if (type.compareTo("CALENDAR_UPDATED_EVENT") == 0) { + return NotificationType.CALENDAR_UPDATED_EVENT; + } + else if (type.compareTo("CALENDAR_DELETED_EVENT") == 0) { + return NotificationType.CALENDAR_DELETED_EVENT; + } + else if (type.compareTo("CALENDAR_ADDED_EVENT") == 0) { + return NotificationType.CALENDAR_ADDED_EVENT; + } + else if (type.compareTo("CALENDAR_UPDATED_EVENT") == 0) { + return NotificationType.CALENDAR_UPDATED_EVENT; + } + else if (type.compareTo("CALENDAR_DELETED_EVENT") == 0) { + return NotificationType.CALENDAR_DELETED_EVENT; + } + else if (type.compareTo("MESSAGE") == 0) { + return NotificationType.MESSAGE; + } + else if (type.compareTo("POST_ALERT") == 0) { + return NotificationType.POST_ALERT; + } + else if (type.compareTo("REQUEST_CONNECTION") == 0) { + return NotificationType.REQUEST_CONNECTION; + } + else if (type.compareTo("JOB_COMPLETED_NOK") == 0) { + return NotificationType.JOB_COMPLETED_NOK; + } + else if (type.compareTo("JOB_COMPLETED_OK") == 0) { + return NotificationType.JOB_COMPLETED_OK; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_EDIT") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_EDIT; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_VIEW") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_VIEW; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_FORWARD_STEP_COMPLETED_OWNER") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_FORWARD_STEP_COMPLETED_OWNER; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_STEP_FORWARD_PEER") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_STEP_FORWARD_PEER; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_STEP_REQUEST_TASK") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_STEP_REQUEST_TASK; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_USER_FORWARD_TO_OWNER") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_USER_FORWARD_TO_OWNER; + } + else if (type.compareTo("DOCUMENT_WORKFLOW_FIRST_STEP_REQUEST_INVOLVMENT") == 0) { + return NotificationType.DOCUMENT_WORKFLOW_FIRST_STEP_REQUEST_INVOLVMENT; + } + else if (type.compareTo("TDM_TAB_RESOURCE_SHARE") == 0) { + return NotificationType.TDM_TAB_RESOURCE_SHARE; + } + else if (type.compareTo("TDM_RULE_SHARE") == 0) { + return NotificationType.TDM_RULE_SHARE; + } + else if (type.compareTo("TDM_TEMPLATE_SHARE") == 0) { + return NotificationType.TDM_TEMPLATE_SHARE; + } + else if (type.compareTo("CAT_ITEM_SUBMITTED") == 0) { + return NotificationType.CAT_ITEM_SUBMITTED; + } + else if (type.compareTo("CAT_ITEM_REJECTED") == 0) { + return NotificationType.CAT_ITEM_REJECTED; + } + else if (type.compareTo("CAT_ITEM_PUBLISHED") == 0) { + return NotificationType.CAT_ITEM_PUBLISHED; + } + else if (type.compareTo("CAT_ITEM_UPDATED") == 0) { + return NotificationType.CAT_ITEM_UPDATED; + } + else if (type.compareTo("CAT_ITEM_DELETE") == 0) { + return NotificationType.CAT_ITEM_DELETE; + } + else if (type.compareTo("GENERIC") == 0) { + return NotificationType.GENERIC; + } + + else + throw new NotificationTypeNotFoundException("The Notification Type was not recognized should be one of " + NotificationType.values() + " asked for: " + type); + } + /** + * + * @param time in milliseconds + * @return a Date object + */ + private Date getDateFromTimeInMillis(String time) { + Long timeInMillis = Long.parseLong(time); + Calendar toSet = Calendar.getInstance(); + toSet.setTimeInMillis(timeInMillis); + return toSet.getTime(); + } + /** + * update the post by incrementing or decrementing by (1) the CommentsNo + * used when adding or removing a comment to a post + * @param toUpdate the postId + * @param increment set true if you want to add 1, false to subtract 1. + */ + private boolean updatePostCommentsCount(Post toUpdate, boolean increment) { + int newCount = 0; + try { + int current = Integer.parseInt(toUpdate.getCommentsNo()); + newCount = increment ? current+1 : current-1; + } + catch (NumberFormatException e) { + _log.error("Comments Number found is not a number: " + toUpdate.getCommentsNo()); + } + CqlSession session = conn.getKeyspaceSession(); + //an entry in the Post CF + try { + session.execute(updatePostEntry(session, COMMENTS_NO).bind((long) newCount, UUID.fromString(toUpdate.getKey()))); + + } catch (Exception e) { + _log.error("CommentsNo update NOT OK "); + + return false; + } + _log.info("CommentsNo update OK to: " + newCount); + return true; + } + + /** + * update the post by incrementing or decrementing by (1) the LikesNo + * used when adding or removing a comment to a post + * @param toUpdate the postId + * @param increment set true if you want to add 1, false to subtract 1. + */ + private boolean updatePostLikesCount(Post toUpdate, boolean increment) { + int newCount = 0; + try { + int current = Integer.parseInt(toUpdate.getLikesNo()); + newCount = increment ? current+1 : current-1; + } + catch (NumberFormatException e) { + _log.error("Likes Number found is not a number: " + toUpdate.getLikesNo()); + } + CqlSession session = conn.getKeyspaceSession(); + //an entry in the post CF + try { + session.execute(updatePostEntry(session, LIKES_NO).bind((long)newCount, UUID.fromString(toUpdate.getKey()))); + + } catch (Exception e) { + _log.error("LikesNo update NOT OK "); + + return false; + } + _log.info("LikesNo update OK to: " + newCount); + return true; + } + + /** + * update the hashtag count by incrementing or decrementing it by (1) + * used when adding or removing a hashtag in a post + * @param vreid the vreid + * @param hashtag the hashtag + * @param increment set true if you want to add 1, false to subtract 1. + */ + private boolean updateVREHashtagCount(String vreid, String hashtag, boolean increment) { + Map vreHashtags = getVREHashtagsWithOccurrence(vreid); + //if the hashtag not yet exist + int newCount = 0; + + if (!vreHashtags.containsKey(hashtag)) { + newCount = 1; + } + else { + try { + int current = vreHashtags.get(hashtag); + newCount = increment ? current+1 : current-1; + } + catch (NumberFormatException e) { + _log.error("Hashtag Number found is not a number: " + newCount); + } + } + _log.debug("Updating counter for " + hashtag + " to " + newCount); + CqlSession session = conn.getKeyspaceSession(); + BoundStatement stmt; + + if (existRecordbyCompId(session, HASHTAG, VRE_ID, hashtag, vreid, HASHTAGS_COUNTER)){ + stmt = session.prepare(QueryBuilder.update(HASHTAGS_COUNTER) + .setColumn(COUNT, QueryBuilder.bindMarker()) + .whereColumn(HASHTAG).isEqualTo(QueryBuilder.bindMarker()) + .whereColumn(VRE_ID).isEqualTo(QueryBuilder.bindMarker()) + .build()).bind((long)newCount, hashtag, vreid); + } + else{ + stmt = createNewUHashtagCounterEntry(session).bind(vreid, hashtag, (long)newCount); + } + BatchStatement writeBatch = getBatch().add(stmt); + try { + session.execute(writeBatch); + + } catch (Exception e) { + _log.error("Hashtag Count update NOT OK "); + + return false; + } + _log.debug("Hashtag Count update OK to: " + newCount); + return true; + } + /** + * verify an email address + * @param email + * @return true or false + */ + private boolean verifyEmail(String email) { + boolean isValid = false; + try { + InternetAddress internetAddress = new InternetAddress(email); + internetAddress.validate(); + isValid = true; + } catch (AddressException e) { + _log.error("Validation Exception Occurred for email: " + email); + } + return isValid; + } + + + @Override + public List getAllVREIds(){ + + Set ids = new HashSet<>(); + + CqlSession session = conn.getKeyspaceSession(); + ResultSet result = null; + PreparedStatement stmtFind = session.prepare(QueryBuilder + .selectFrom(VRE_TIMELINE_POSTS).column(VRE_ID).all() + .build()); + try { + result = session.execute(stmtFind.bind()); + + } catch (Exception e){ + e.printStackTrace(); + + } + + Listrows=result.all(); + for (Row row : rows) { + ids.add(row.getString(VRE_ID)); + } + List toReturn = new ArrayList<>(); + toReturn.addAll(ids); + _log.debug("VRE ids are " + ids); + + return toReturn; + } +} diff --git a/src/main/java/org/gcube/social_networking/server/SocialDBDriver.java b/src/main/java/org/gcube/social_networking/server/SocialDBDriver.java new file mode 100644 index 0000000..f094306 --- /dev/null +++ b/src/main/java/org/gcube/social_networking/server/SocialDBDriver.java @@ -0,0 +1,449 @@ +package org.gcube.social_networking.server; + +import java.util.List; +import java.util.Map; + +import javax.mail.internet.AddressException; + +import org.gcube.social_networking.socialnetworking.model.shared.Attachment; +import org.gcube.social_networking.socialnetworking.model.shared.Comment; +import org.gcube.social_networking.socialnetworking.model.shared.Invite; +import org.gcube.social_networking.socialnetworking.model.shared.InviteOperationResult; +import org.gcube.social_networking.socialnetworking.model.shared.InviteStatus; +import org.gcube.social_networking.socialnetworking.model.shared.Like; +import org.gcube.social_networking.socialnetworking.model.shared.Notification; +import org.gcube.social_networking.socialnetworking.model.shared.NotificationChannelType; +import org.gcube.social_networking.socialnetworking.model.shared.NotificationType; +import org.gcube.social_networking.socialnetworking.model.shared.Post; +import org.gcube.social_networking.socialnetworking.model.shared.RangePosts; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.ColumnNameNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.CommentIDNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.PostIDNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.PostTypeNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.InviteIDNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.InviteStatusNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.LikeIDNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.NotificationChannelTypeNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.NotificationIDNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.NotificationTypeNotFoundException; +import org.gcube.social_networking.socialnetworking.model.shared.exceptions.PrivacyLevelTypeNotFoundException; + +/** + * @author Massimiliano Assante ISTI-CNR + * @author Costantino Perciante ISTI-CNR + * DatabookStore is the high level interface for querying and adding data to DatabookStore + */ +public interface SocialDBDriver { + /** + * save a Post instance in the store + * @return true if everything went fine + */ + boolean saveUserPost(Post post); + /** + * Save a Post instance in the store having more than one attachment + * Use this if you need to attach more than one file to the post + * + * @param attachments a list of attachments starting from the second + * @return true if everything went fine + */ + boolean saveUserPost(Post post, List attachments); + /** + * delete a post from the store + * @throws PostIDNotFoundException + * @return true if everything went fine + */ + boolean deletePost(String postid) throws PostIDNotFoundException, PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException; + /** + * save a post in the VRES TimeLine in the store + * @param postKey the post id + * @param vreid vre identifier + * @throws PostIDNotFoundException + */ + boolean savePostToVRETimeline(String postKey, String vreid) throws PostIDNotFoundException; + /** + * save a Post instance in the store + * @return true if everything went fine + */ + boolean saveAppPost(Post post); + /** + * Save a Post instance in the store + * Use this if your app needs to attach more than one file to the post + * + * @param attachments a list of attachments starting from the second + * @return true if everything went fine + */ + boolean saveAppPost(Post post, List attachments); + /** + * read a post from a given id + * @throws PrivacyLevelTypeNotFoundException + * @throws PostTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + Post readPost(String postid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException; + /** + * @param userid user identifier + * return all the posts belonging to the userid + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getAllPostsByUser(String userid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException; + /** + * @param appid application identifier + * return all the posts belonging to the appid + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getAllPostsByApp(String appid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException; + /** + * @param userid the user identifier like andrea.rossi + * @param timeInMillis the initial time in millis to be considered + * @return a list of posts commented by userid starting from timeInMillis + * @throws Exception + */ + List getRecentCommentedPostsByUserAndDate(String userid, long timeInMillis) throws Exception; + /** + * return all the posts whose Level is PORTAL + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + * @throws PrivacyLevelTypeNotFoundException + */ + List getAllPortalPrivacyLevelPosts() throws PostTypeNotFoundException, ColumnNameNotFoundException, PrivacyLevelTypeNotFoundException; + /** + * return the most recent posts for this user up to quantity param + * @param userid user identifier + * @param quantity the number of most recent posts for this user + * @return a List of most recent posts for this user + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getRecentPostsByUser(String userid, int quantity) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException; + /** + * @param vreid vre identifier + * return all the posts belonging to the userid + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getAllPostsByVRE(String vreid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException; + /** + * return the most recent posts for this vre up to quantity param + * @param vreid VRES identifier + * @param quantity the number of most recent posts for this vre + * @return a List of most recent posts for this vre + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getRecentPostsByVRE(String vreid, int quantity) throws IllegalArgumentException, PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException; + /** + * return the most recent posts for this vre up to quantity param and the last index of the posts in the timeline + * lastReturnedPostTimelineIndex is useful to know from where to start the range the next time you ask, because there are deletions + * + * @param vreid VRES identifier + * @param from the range start (most recent posts for this vre) has to be greater than 0 + * @param quantity the number of most recent posts for this vre starting from "from" param + * @return a RangePosts containing of most recent posts for this vre + * @throws PostTypeNotFoundException + * @throws PrivacyLevelTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + RangePosts getRecentPostsByVREAndRange(String vreid, int from, int quantity) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, PostIDNotFoundException; + /** + * @param userid user identifier + * @param timeInMillis time in milliseconds from which you want to start retrieve the posts + * @return the number of posts in the range from: today to: timeInMillis + */ + List getRecentPostsByUserAndDate(String userid, long timeInMillis) throws IllegalArgumentException; + /** + * save a Notification instance in the store + * @return true if everything went fine + */ + boolean saveNotification(Notification notification); + /** + * set an existing Notification instance in the to read + * @return true if everything went fine + */ + boolean setNotificationRead(String notificationidToSet) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException; + /** + * read a notification from a given id + * @throws {@link ColumnNameNotFoundException} + * @throws {@link NotificationIDNotFoundException} + * @throws {@link NotificationTypeNotFoundException} + */ + Notification readNotification(String notificationid) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException; + + /** + * @param userid user identifier + * @param limit set 0 to get everything, an int to get the most recent -limit- notifications + * return all the notifications belonging to the userid up to limit, set 0 to get everything + * @throws NotificationTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getAllNotificationByUser(String userid, int limit) throws NotificationTypeNotFoundException, ColumnNameNotFoundException, NotificationIDNotFoundException; + /** + * + * @param userid user identifier + * @param from the range start has to be greater than 0 + * @param quantity the number of most recent notifications for this user starting from "from" param + * @return all the notifications for the userid in the range requested + * @throws NotificationTypeNotFoundException + * @throws ColumnNameNotFoundException + * @throws NotificationIDNotFoundException + */ + List getRangeNotificationsByUser(String userid, int from, int quantity) throws NotificationTypeNotFoundException, ColumnNameNotFoundException, NotificationIDNotFoundException; + /** + * This is a fast way to set all notification to read quickly + * @param userid + * @return true if everything went fine + * @throws {@link ColumnNameNotFoundException} + * @throws {@link NotificationIDNotFoundException} + * @throws {@link NotificationTypeNotFoundException} + */ + boolean setAllNotificationReadByUser(String userid) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException; + /** + * return the not yet read notifications (not including messages) + * @param userid user identifier + * @return a List of not yet read notifications for this user + * @throws NotificationTypeNotFoundException + * @throws ColumnNameNotFoundException + */ + List getUnreadNotificationsByUser(String userid) throws NotificationTypeNotFoundException, ColumnNameNotFoundException, NotificationIDNotFoundException; + /** + * + * @param userid user identifier + * @throws ColumnNameNotFoundException + * @throws NotificationTypeNotFoundException + * @throws NotificationIDNotFoundException + * @return true if there are unread notifications (not including messages), false if they are all read + */ + boolean checkUnreadNotifications(String userid) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException; + /** + * + * @param userid user identifier + * @throws ColumnNameNotFoundException + * @throws NotificationTypeNotFoundException self explaining + * @throws NotificationChannelTypeNotFoundException self explaining + * @throws NotificationIDNotFoundException + * @return true if there are unread messages notifications (including messages), false if they are all read + */ + boolean checkUnreadMessagesNotifications(String userid) throws NotificationIDNotFoundException, NotificationTypeNotFoundException, ColumnNameNotFoundException; + + /** + * return the channels a user chose for being notified for a given notification type + * @param userid user identifier + * @param notificationType the type of the notification + * @return a list of NotificationChannelType that represents the channels this user wants to be notified + */ + List getUserNotificationChannels(String userid, NotificationType notificationType) throws NotificationChannelTypeNotFoundException, NotificationTypeNotFoundException; + /** + * set the notification preferences map (enable or disable the channels to be used for notifying the user) + * @param userid user identifier + * @param enabledChannels a map of the channels to which reach the user per notification, empty array or null values to set the key notification type off + * @return true if everything was fine + */ + boolean setUserNotificationPreferences(String userid, Map enabledChannels); + /** + * get the notification preferences map (enableor disable the channels to be used for notifying the user) + * @param userid user identifier + * @return the map + * @throws NotificationTypeNotFoundException self explaining + * @throws NotificationChannelTypeNotFoundException self explaining + */ + Map getUserNotificationPreferences(String userid) throws NotificationTypeNotFoundException, NotificationChannelTypeNotFoundException; + + /** + * @param commentId comment unique identifier + * @return the comment belonging to the commentId + * @throws CommentIDNotFoundException + */ + Comment readCommentById(String commentId) throws CommentIDNotFoundException; + /** + * add a comment to a post + * @param comment the Comment instance to add + */ + boolean addComment(Comment comment) throws PostIDNotFoundException; + /** + * @param postid the post identifier + * return all the comments belonging to the postid + */ + List getAllCommentByPost(String postid); + /** + * @param userid user identifier + * @param timeInMillis time in milliseconds from which you want to start retrieve the posts + * @return a list of comments (sorted starting from the most recent one) made by the user since timeInMillis up to now + */ + List getRecentCommentsByUserAndDate(String userid, long timeInMillis) throws Exception; + /** + * edit a comment + * @param comment the comment to edit + * @return true if success, false otherwise + */ + boolean editComment(Comment comment) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, CommentIDNotFoundException, PostIDNotFoundException; + /** + * deletes a comment + * @param commentid the comment identifier to delete + * @param postid the postid to which the comment is associated + * @return true if success, false otherwise + */ + boolean deleteComment(String commentid, String postid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, CommentIDNotFoundException, PostIDNotFoundException; + /** + * add a like to a post + * @param like instance + * @throws PostIDNotFoundException + */ + boolean like(Like like) throws PostIDNotFoundException; + /** + * unlike a post + * @param userid user identifier + * @param likeid the like identifier to delete + * @param postid the postid to which the comment is associated + * @return true if success, false otherwise + */ + boolean unlike(String userid, String likeid, String postid) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, ColumnNameNotFoundException, LikeIDNotFoundException, PostIDNotFoundException; + /** + * @param userid user identifier + * return all the postids a user has liked + */ + List getAllLikedPostIdsByUser(String userid); + /** + * @param userid user identifier + * @param limit set 0 to get everything, an int to get the most recent -limit- liked posts + * @throws ColumnNameNotFoundException . + * @throws PostIDNotFoundException . + * @throws PostTypeNotFoundException . + * @throws PrivacyLevelTypeNotFoundException + * return all the posts a user has liked + */ + List getAllLikedPostsByUser(String userid, int limit) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException; + /** + * @param userid user identifier + * @param timeInMillis time in milliseconds from which you want to start retrieve the posts + * @return the likes made to posts in the range from: today to: timeInMillis + */ + List getRecentLikedPostsByUserAndDate(String userid, long timeInMillis) throws IllegalArgumentException; + /** + * @param postid postid identifier + * return all the likes belonging to the postid + */ + List getAllLikesByPost(String postid); + /** + * + * @param hashtags the hashtag including the '#' + * @param postid the postid to which the hashtag is associated + * @param vreid VRE identifier + * @return true if success, false otherwise + * @throws PostIDNotFoundException + */ + boolean saveHashTags(String postid, String vreid, List hashtags) throws PostIDNotFoundException; + /** + * + * @param hashtags the hashtag including the '#' + * @param commentId the commentId to which the hashtag is associated + * @param vreid VRE identifier + * @return true if success, false otherwise + * @throws CommentIDNotFoundException + */ + boolean saveHashTagsComment(String commentId, String vreid, List hashtags) throws CommentIDNotFoundException; + /** + * + * @param hashtags the hashtag including the '#' + * @param postid the postid to which the hashtag is associated + * @param vreid VRE identifier + * @return true if success, false otherwise + * @throws PostIDNotFoundException + */ + boolean deleteHashTags(String postid, String vreid, List hashtags) throws PostIDNotFoundException; + /** + * + * @param hashtags the hashtag including the '#' + * @param commentId the commentId to which the hashtag is associated + * @param vreid VRE identifier + * @return true if success, false otherwise + * @throws CommentIDNotFoundException + */ + boolean deleteHashTagsComment(String commentId, String vreid, List hashtags) throws CommentIDNotFoundException; + /** + * get a map of vre hashtags where the key is the hashtag and the value is the occurrence of the hashtag in the VRE + * @param vreid vre identifier (scope) + * @return a HashMap of vre Hashtags associated with their occurrence + */ + Map getVREHashtagsWithOccurrence(String vreid); + /** + * get a map of vre hashtags where the key is the hashtag and the value is the occurrence of the hashtag in the VRE + * @param vreid vre identifier (scope) + * @param timestamp do not consider hashtags used before timestamp + * @return a HashMap of vre Hashtags associated with their occurrence + */ + Map getVREHashtagsWithOccurrenceFilteredByTime(String vreid, long timestamp); + /** + * + * @param vreid VRE identifier + * @param hashtag the hashtag to look for including the '#', it is case sensitive + * @throws ColumnNameNotFoundException . + * @throws PostIDNotFoundException . + * @throws PostTypeNotFoundException . + * @throws PrivacyLevelTypeNotFoundException + * @return all the posts having the hashtag passed as parameter + */ + List getVREPostsByHashtag(String vreid, String hashtag) throws PrivacyLevelTypeNotFoundException, PostTypeNotFoundException, PostIDNotFoundException, ColumnNameNotFoundException; + + /** + * Save the invite for a given email into a given vre + * @param invite the invite object instanc to save + * @return {@link InviteOperationResult} SUCCESS, FAILED or ALREADY_INVITED (if an invite is sent to en existing email in the same environment more than once) + */ + InviteOperationResult saveInvite(Invite invite) throws AddressException; + /** + * + * @param vreid the environment where you want to check the invite + * @param email the email of the invite to check in the environmnet + * @return the InviteId if present, null otherwise + */ + String isExistingInvite(String vreid, String email); + /** + * read an invite from a given id + * @throws InviteIDNotFoundException + * @throws InviteStatusNotFoundException + */ + Invite readInvite(String inviteid) throws InviteIDNotFoundException, InviteStatusNotFoundException; + /** + * set the status of an invite, see {@link InviteStatus} + * @throws InviteIDNotFoundException + */ + boolean setInviteStatus(String vreid, String email, InviteStatus status) throws InviteIDNotFoundException, InviteStatusNotFoundException; + /** + * Use to get the list of invites per VRE + * @param vreid the vre id + * @param status optional, if you want to restict on the status, e.g. all pending invites + * @return return the list of invites + * @throws InviteIDNotFoundException + * @throws InviteStatusNotFoundException + */ + List getInvitedEmailsByVRE(String vreid, InviteStatus... status) throws InviteIDNotFoundException, InviteStatusNotFoundException; + /** + * + * @param postId + * @return the list of attachments of the post postId, starting from the second one (first attachment is included in post instance already) + */ + List getAttachmentsByPostId(String postId) throws PostIDNotFoundException; + + /** + * save an attachment + */ + boolean saveAttachmentEntry(String postId, Attachment toSave); + + /** + * Retrieve all the ids of the vre + * @return the set of ids of the vre available or empty list in case of errors. + */ + public List getAllVREIds(); + + /** + * close the connection to the underlying database + */ + void closeConnection(); +} + diff --git a/src/main/java/org/gcube/social_networking/utils/Schema.java b/src/main/java/org/gcube/social_networking/utils/Schema.java new file mode 100644 index 0000000..b9a97ed --- /dev/null +++ b/src/main/java/org/gcube/social_networking/utils/Schema.java @@ -0,0 +1,76 @@ +package org.gcube.social_networking.utils; + +/** + * @author Massimiliano Assante ISTI-CNR + * @author Ahmed Ibrahim ISTI-CNR + * + * @version 2.0.0 October 2023 + * + */ + +public class Schema { + //Tables + public static final String NOTIFICATIONS = "Notifications"; + public static final String POSTS = "Posts"; + public static final String COMMENTS = "Comments"; + public static final String LIKES = "Likes"; + public static final String INVITES = "Invites"; + public static final String VRE_TIMELINE_POSTS = "VRETimeline"; + public static final String USER_TIMELINE_POSTS = "UserTimeline"; + public static final String APP_TIMELINE_POSTS = "AppTimeline"; + public static final String USER_LIKED_POSTS = "UserLikes"; + public static final String USER_NOTIFICATIONS = "UserNotifications"; // regular user notifications timeline (both read and unread, messages are included) + public static final String USER_NOTIFICATIONS_UNREAD = "UserUnreadNotifications"; // only unread user notifications/ notifications messages + public static final String USER_NOTIFICATIONS_PREFERENCES = "UserNotificationsPreferences"; // preferences for notifications + public static final String HASHTAGS_COUNTER = "HashtagsCounter"; // count the hashtags per group and type + public static final String HASHTAGGED_POSTS = "HashtaggedPosts"; // contains hashtags per type associated with vre and POST + public static final String HASHTAGGED_COMMENTS = "HashtaggedComments"; // contains hashtags per type associated with vre and comment + public static final String VRE_INVITES = "VREInvites"; //contains the emails that were invited per VRE + public static final String EMAIL_INVITES = "EmailInvites"; //contains the list of invitation per email + public static final String ATTACHMENTS = "Attachments"; //contains the list of all the attachments in a POST + + //columns + public static final String USER_ID = "userid"; //text + public static final String TYPE = "type"; //text + public static final String PREFERENCE = "preference"; //text + public static final String TIMESTAMP = "timestamp"; //timestamp + public static final String NOT_ID = "notid"; //UUID + public static final String VRE_ID = "vreid"; //text + public static final String POST_ID = "postid"; //UUID + public static final String APP_ID = "appid"; //text + public static final String HASHTAG = "hashtag"; //text + public static final String COMMENT_ID = "commentid"; //UUID + public static final String COUNT = "count"; //big int + public static final String LIKE_ID = "likeid"; //UUID + public static final String INVITE_ID = "inviteid"; //UUID + public static final String STATUS = "status"; //text + public static final String EMAIL = "email"; //text + public static final String ATTACH_ID = "attachid"; //UUID + public static final String URI = "uri"; //text + public static final String NAME = "name"; //text + public static final String DESCRIPTION = "description"; //text + public static final String URI_THUMBNAIL = "urithumbnail"; //text + public static final String MIME_TYPE = "mimetype"; //text + public static final String SENDER_USER_ID = "senderuserid"; //text + public static final String CONTROL_CODE = "controlcode"; //text + public static final String SENDER_FULL_NAME = "senderfullname"; //text + public static final String FULL_NAME = "fullname"; //text + public static final String THUMBNAIL_URL = "thumbnailurl"; //text + public static final String COMMENT = "comment"; //text + public static final String IS_EDIT = "isedit"; //bool + public static final String LAST_EDIT_TIME = "lastedittime"; //timestamp + public static final String SUBJECT_ID = "subjectid"; //text + public static final String SENDER_ID = "senderid"; //text + public static final String SENDER_THUMBNAIL_URL = "senderthumbnailurl"; //text + public static final String IS_READ = "isread"; //bool + public static final String LINK_HOST = "linkhost"; //text + public static final String LIKES_NO = "likesno"; //big int + public static final String LINK_DESCRIPTION = "linkdescription"; //text + public static final String IS_APPLICATION_POST = "isapplicationpost"; //bool --> + public static final String ENTITY_ID = "entityid"; //text + public static final String PRIVACY = "privacy"; //text + public static final String MULTI_FILE_UPLOAD = "multifileupload"; //bool + public static final String COMMENTS_NO = "commentsno"; //big int + public static final String LINK_TITLE = "linktitle"; //text + +} \ No newline at end of file diff --git a/src/main/resources/META-INF/enunciate/d4science_docs.fmt b/src/main/resources/META-INF/enunciate/d4science_docs.fmt new file mode 100644 index 0000000..a5ee6ab --- /dev/null +++ b/src/main/resources/META-INF/enunciate/d4science_docs.fmt @@ -0,0 +1,1183 @@ +[#ftl] +[#-- + + Copyright © 2006-2016 Web Cohesion (info@webcohesion.com) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--] +[#-- @ftlvariable name="resourceApis" type="java.util.List" --] +[#-- @ftlvariable name="serviceApis" type="java.util.List" --] +[#-- @ftlvariable name="data" type="java.util.List" --] +[#-- @ftlvariable name="downloads" type="java.util.List" --] +[#-- @ftlvariable name="title" type="java.lang.String" --] +[#-- @ftlvariable name="indexPageName" type="java.lang.String" --] +[#-- @ftlvariable name="disableMountpoint" type="java.lang.Boolean" --] +[#-- @ftlvariable name="disableResourceLinks" type="java.lang.Boolean" --] +[#-- @ftlvariable name="apiRelativePath" type="java.lang.String" --] +[#-- @ftlvariable name="cssFile" type="java.lang.String" --] +[#-- @ftlvariable name="additionalCssFiles" type="java.util.List" --] +[#-- @ftlvariable name="copyright" type="java.lang.String" --] +[#-- @ftlvariable name="apiDoc" type="java.lang.String" --] +[#-- @ftlvariable name="swaggerUI" type="com.webcohesion.enunciate.api.InterfaceDescriptionFile" --] +[#-- @ftlvariable name="favicon" type="java.lang.String" --] +[#-- @ftlvariable name="includeApplicationPath" type="java.lang.Boolean" --] +[#-- @ftlvariable name="includeDataTypesHomeIndex" type="java.lang.Boolean" --] +[#--set up the subnavigation menus--] +[#assign nav_sections = { } /] +[#if resourceApis?size > 0] + [#assign nav_sections = nav_sections + { "Resources" : "resources.html" }/] +[/#if] +[#if serviceApis?size > 0] + [#assign nav_sections = nav_sections + { "Services" : "services.html" }/] +[/#if] +[#if data?size > 0] + [#list data as syntax] + [#assign nav_sections = { syntax.label : syntax.slug + ".html" } /] + [/#list] +[/#if] +[#if downloads?size > 0] + [#assign nav_sections = nav_sections + { "Files and Libraries" : "downloads.html"} /] +[/#if] +[#--Basic boilerplate macro.--] +[#macro boilerplate title=title breadcrumbs=[{"title" : "Home", "href" : indexPageName}] pagenav=[] codeblocks=true] + + + + + + + + + ${title} + + + + + + + + [#if cssFile??] + + + [/#if] + [#list additionalCssFiles as additionalCssFile] + + [/#list] + [#if favicon??] + + + [/#if] + + + + + + +
+ D4Science + D4Science + + Don't have a D4Science account? + + Create one + + Could not find what you are looking for? + + Contact us. +
+ + + +
+
+ + +
+ + + [#nested/] + +
+
+

[#if copyright??]Copyright © ${copyright}. [/#if]Generated by Enunciate.

+
+
+ +
+
+
+ + + + + + + + + + + + + + +[/#macro] +[#--Macro that wraps its text in a deprecated tag if the element is deprecated.--] +[#macro deprecation element] + [#assign deprecated=(element?? && element.deprecated??)/] + [#if deprecated][/#if][#nested/][#if deprecated][/#if] +[/#macro] +[@file name=indexPageName] + [#assign pagenav=[]/] + [#if resourceApis?size > 0] + [#assign pagenav=pagenav + [{ "href" : "#resources", "title" : "Resources" }]/] + [/#if] + [#if serviceApis?size > 0] + [#assign pagenav=pagenav + [{ "href" : "#services", "title" : "Services" }]/] + [/#if] + [#list data as syntax] + [#assign pagenav=pagenav + [{ "href" : "#" + syntax.slug, "title" : syntax.label }]/] + [/#list] + [@boilerplate pagenav=pagenav] + [#if apiDoc??] + + [/#if] + [#if resourceApis?size > 0] + +

Resources

+ [#list resourceApis as resourceApi] + [#if downloads?size > 0] + +

+ The resources use a data model that is supported by a set of client-side libraries that are made available on the + files and libraries page. +

+ [/#if] + [#if resourceApi.wadlFile??] + +

+ There is a WADL document available that describes the resources API. +

+ [/#if] + [#if swaggerUI??] + +

+ You may also enjoy the interactive interface provided for this API by Swagger. +

+

+ Try it out! +

+ [/#if] + + + + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + + + [#list resourceApi.resourceGroups as resourceGroup] + [@processResourceGroup resourceGroup=resourceGroup/] + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + [/#list] + +
namepathmethodsdescription
[@deprecation element=resourceGroup]${resourceGroup.label}[/@deprecation]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${path.path}[/@deprecation]
  • [/#list]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#list path.methods as method]${method} [/#list][/@deprecation]
  • [/#list]
[@deprecation element=resourceGroup]${resourceGroup.description!" "}[/@deprecation]
+ [/#list] + [/#if] + [#if serviceApis?size > 0] + +

Services

+ [#list serviceApis as serviceApi] + [#list serviceApi.serviceGroups as serviceGroup] + + + + + + + + + + + [#list serviceGroup.services as service] + [@processService service=service/] + + + + + [/#list] + +
Namespace ${serviceGroup.namespace!"(Default)"}[#if serviceGroup.wsdlFile??] (wsdl)[/#if]
namedescription
[@deprecation element=service]${service.label}[/@deprecation][@deprecation element=service]${service.description!" "}[/@deprecation]
+ [/#list] + [/#list] + [#if downloads?size > 0] + +

The services API is also accessible by a set of client-side libraries that can be downloaded from the files and libraries page.

+ [/#if] + [/#if] + [#if data?size > 0 && includeDataTypesHomeIndex] + +

Data Types

+ [#list data as syntax] + [@processDataSyntax syntax=syntax/] + +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/#list] + [#elseif data?size > 0] + [#list data as syntax] + [@processDataSyntax syntax=syntax/] + [/#list] + [/#if] + [/@boilerplate] +[/@file] +[@file name="data.html"] + [#assign pagenav=[]/] + [#list data as syntax] + [#assign pagenav=pagenav + [{ "href" : "#" + syntax.slug, "title" : syntax.label }]/] + [/#list] + [@boilerplate title=title + ": Data Types" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Data Types" , "href" : "data.html"}] pagenav=pagenav] +

Data Types

+ [#list data as syntax] + +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/#list] + [/@boilerplate] +[/@file] +[#if downloads?size > 0] + [@file name="downloads.html"] + [#assign pagenav=[]/] + [#list downloads as download] + [#assign pagenav=pagenav + [{ "href" : "#" + download.slug, "title" : download.name }]/] + [/#list] + [@boilerplate title=title + ": Files and Libraries" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, { "title" : "Files and Libraries" , "href" : "downloads.html"}] codeblocks=true pagenav=pagenav] +

Files and Libraries

+ + [#list downloads as download] +

${download.name}

+ [#if download.created??] +

Created ${download.created?date?string.long}

+ [/#if] + [#if download.artifactId??] +
+ [#if download.groupId??] +
groupId
+
${download.groupId}
+ [/#if] + [#if download.artifactId??] +
artifactId
+
${download.artifactId}
+ [/#if] + [#if download.version??] +
version
+
${download.version}
+
+ [/#if] + [/#if] + [#if download.description??] +

${download.description}

+ [/#if] + + + + + + + + + + + [#list download.files as file] + + + + + + [/#list] + +
Files
namesizedescription
${file.name}${file.size}${file.description!download.description!" "}
+ [/#list] + [/@boilerplate] + [/@file] +[/#if] +[#if resourceApis?size > 0] + [@file name="resources.html"] + [@boilerplate title=title + ": Resources" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Resources" , "href" : "resources.html"}]] +

Resources

+ + [#list resourceApis as resourceApi] + [#if downloads?size > 0] +

+ The resources use a data model that is supported by a set of client-side libraries that are made available on the + files and libraries page. +

+ [/#if] + [#if resourceApi.wadlFile??] +

+ There is a WADL document available that describes the resources API. +

+ [/#if] + [#if swaggerUI??] +

+ You may also enjoy the interactive interface provided for this API by Swagger. +

+

+ Try it out! +

+ [/#if] + + + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + + + [#list resourceApi.resourceGroups as resourceGroup] + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + [/#list] + +
namepathmethodsdescription
[@deprecation element=resourceGroup]${resourceGroup.label}[/@deprecation]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${path.path}[/@deprecation]
  • [/#list]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#list path.methods as method]${method} [/#list][/@deprecation]
  • [/#list]
[@deprecation element=resourceGroup]${resourceGroup.description!" "}[/@deprecation]
+ [/#list] + [/@boilerplate] + [/@file] +[/#if] +[#if serviceApis?size > 0] + [@file name="services.html"] + [@boilerplate title=title + ": Services" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Services" , "href" : "services.html"}]] +

Services

+ [#list serviceApis as serviceApi] + [#list serviceApi.serviceGroups as serviceGroup] + + + + + + + + + + + [#list serviceGroup.services as service] + + + + + [/#list] + +
Namespace ${serviceGroup.namespace}[#if serviceGroup.wsdlFile??] (wsdl)[/#if]
namedescription
[@deprecation element=service]${service.label}[/@deprecation][@deprecation element=service]${service.description!" "}[/@deprecation]
+ [/#list] + [/#list] + [#if downloads?size > 0] + +

The services API is also accessible by a set of client-side libraries that can be downloaded from the files and libraries page.

+ [/#if] + [/@boilerplate] + [/@file] +[/#if] +[#macro processResourceGroup resourceGroup] + [#assign pagenav=[]/] + [#list resourceGroup.resources as resource] + [#list resource.methods as method] + [#assign path=resource.path/] + [#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))] + [#assign path="/" + resourceGroup.relativeContextPath + resource.path/] + [/#if] + [#assign pagenav=pagenav + [{ "href" : "#" + method.slug, "title" : method.label + " " + path }]/] + [/#list] + [/#list] + [#-- @ftlvariable name="resourceGroup" type="com.webcohesion.enunciate.api.resources.ResourceGroup" --] + [@file name=resourceGroup.slug + ".html"] + [@boilerplate title=title + ": " + resourceGroup.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Resources" , "href" : "resources.html"}, {"title" : resourceGroup.label , "href" : resourceGroup.slug + ".html"}] pagenav=pagenav] +

${resourceGroup.label} Resource

+ [#if resourceGroup.description??] + +

${resourceGroup.description}

+ [/#if] + [#list resourceGroup.resources as resource] + [#if resource.since?? || resource.version?? || resource.seeAlso??] + +
+ [#if resource.since??] +
Available Since
+
${resource.since}
+ [/#if] + [#if resource.version??] +
Version
+
${resource.version}
+ [/#if] + [#if resource.seeAlso??] + [#list resource.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#list resource.methods as method] + +
+

${method.label} [#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${resource.path}[#if !disableResourceLinks!false] [/#if]

+ [#if resourceGroup.deprecated?? || method.deprecated??] + +
This method has been deprecated. [#if method.deprecated??] ${method.deprecated!""}[#else] ${resource.deprecated!""}[/#if]
+ [/#if] + [#if method.description??] + +

${method.description}

+ [/#if] + [#-- would be nice to enable a "Try it out" link to Swagger. See https://github.com/swagger-api/swagger-spec/issues/239 + [#if swaggerUI??] + +

Try it out!

+ [/#if] + --] + [#assign securityRoles=method.securityRoles![]/] + [#if (method.since?? || method.version?? || method.seeAlso?? || securityRoles?size > 0)] + +
+ [#if method.since??] +
Available Since
+
${method.since}
+ [/#if] + [#if method.version??] +
Version
+
${method.version}
+ [/#if] + [#if securityRoles?size > 0] +
Security Roles Allowed
+
[#list securityRoles as role]${role}[#if role_has_next], [/#if][/#list]
+ [/#if] + [#if method.seeAlso??] + [#list method.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#if method.parameters?size > 0] + + + + + + + + + [#assign includeDefault=method.includeDefaultParameterValues/] + [#if includeDefault] + + [/#if] + [#assign includeConstraints=method.hasParameterConstraints/] + [#if includeConstraints] + + [/#if] + [#assign includeMultiplicity=method.hasParameterMultiplicity/] + [#if includeMultiplicity] + + [/#if] + + + + [#list method.parameters as parameter] + + + + + [#if includeDefault] + + [/#if] + [#if includeConstraints] + + [/#if] + [#if includeMultiplicity] + + [/#if] + + [/#list] + +
Request Parameters
nametypedescriptiondefaultconstraintsmultivalued
${parameter.name}${parameter.typeLabel}${parameter.description!" "}${parameter.defaultValue!" "}${parameter.constraints!" "}${parameter.multivalued?string("yes", "no")}
+ [/#if] + [#if method.requestEntity??] + + + + + + + + [#if method.requestEntity.description??] + + [/#if] + + + + [#list method.requestEntity.mediaTypes as io] + + + + [#if io_index = 0 && method.requestEntity.description??] + 1] rowspan="${method.requestEntity.mediaTypes?size}" class="multi-row-description"[/#if]>${method.requestEntity.description} + [/#if] + + [/#list] + +
Request Body
media typedata typedescription
${io.mediaType}[@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
+ [/#if] + [#if method.responseCodes?size > 0] + + + + + + + + [#assign hasExpectedTypes=false/] + [#list method.responseCodes as responseCode] + [#if responseCode.mediaTypes?size > 0] + [#assign hasExpectedTypes=true/] + [/#if] + [/#list] + [#if hasExpectedTypes] + + [/#if] + + + + [#list method.responseCodes as responseCode] + + + + [#if hasExpectedTypes] + + [/#if] + + [/#list] + +
Response Codes
codeconditiontype
${responseCode.code}${responseCode.condition!""}
    [#list responseCode.mediaTypes as io]
  • [@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
  • [/#list]
+ [/#if] + [#if method.responseEntity??] + + + + + + + + [#if method.responseEntity.description??] + + [/#if] + + + + [#list method.responseEntity.mediaTypes as io] + + + + [#if io_index = 0 && method.responseEntity.description??] + 1] rowspan="${method.responseEntity.mediaTypes?size}" class="multi-row-description"[/#if]>${method.responseEntity.description} + [/#if] + + [/#list] + +
Response Body
media typedata typedescription
${io.mediaType}[@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
+ [/#if] + [#if method.warnings?size > 0] + + + + + + + + + + + [#list method.warnings as responseCode] + + + + + [/#list] + +
Response Warnings
codecondition
${responseCode.code}${responseCode.condition!""}
+ [/#if] + [#if method.responseHeaders?size > 0] + + + + + + + + + + + [#list method.responseHeaders as header] + + + + + [/#list] + +
Response Headers
namedescription
${header.name}${header.description!" "}
+ + [/#if] + [#if method.example??] +

Example

+ +
+
+
+
Request
+
+${method.example.requestHeaders?xhtml}
+            [#if method.example.requestLang??]
+                
+${method.example.requestBody?xhtml}
+                
+            [/#if]
+              
+
+
+
Response
+
+${method.example.responseHeaders?xhtml}
+            [#if method.example.responseLang??]
+                
+${method.example.responseBody?xhtml}
+                
+            [/#if]
+              
+
+
+
+ [/#if] +
+ [/#list] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processService service] + [#assign pagenav=[]/] + [#list service.operations as operation] + [#assign pagenav=pagenav + [{ "href" : "#" + operation.slug, "title" : operation.name }]/] + [/#list] + [#-- @ftlvariable name="service" type="com.webcohesion.enunciate.api.services.Service" --] + [@file name=service.slug + ".html"] + [@boilerplate title=title + ": " + service.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : service.label , "href" : service.slug + ".html"}] pagenav=pagenav] +

${service.label} Service

+ [#if service.deprecated??] + +
This service has been deprecated. ${service.deprecated}
+ [/#if] + [#if service.description??] + +

${service.description}

+ [/#if] + +
+ [#if service.namespace?? && service.namespace?length > 0] +
Namespace
+
${service.namespace}
+ [/#if] + [#if service.group.wsdlFile??] +
WSDL
+
${service.group.wsdlFile.href}
+ [/#if] + [#if service.path??] +
Path
+
${service.path}
+ [/#if] + [#if service.since??] +
Available Since
+
${service.since}
+ [/#if] + [#if service.version??] +
Version
+
${service.version}
+ [/#if] + [#if service.seeAlso??] + [#list service.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [#list service.operations as operation] + +

${operation.name} Operation

+ [#if operation.deprecated??] + +
This method has been deprecated. ${operation.deprecated}
+ [/#if] + [#if operation.description??] + +

${operation.description}

+ [/#if] + [#if (operation.since?? || operation.version?? || operation.seeAlso??)] + +
+ [#if operation.since??] +
Available Since
+
${operation.since}
+ [/#if] + [#if operation.version??] +
Version
+
${operation.version}
+ [/#if] + [#if operation.seeAlso??] + [#list operation.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#if operation.inputParameters?size > 0] + + + + + + + + + + + + [#list operation.inputParameters as parameter] + + + + + + [/#list] + +
Input Parameters
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.httpRequestHeaders?size > 0] + + + + + + + + + + + + [#list operation.httpRequestHeaders as parameter] + + + + + + [/#list] + +
HTTP Request Headers
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.outputParameters?size > 0] + + + + + + + + + + + + [#list operation.outputParameters as parameter] + + + + + + [/#list] + +
Output Parameters
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.returnType??] + + + + + + + + + + + + + + + +
Return Value
typedescription
[@referenceDataType referenceType=operation.returnType/]${operation.returnDescription!" "}
+ [/#if] + [#if operation.faults?size > 0] + + + + + + + + + + + + [#list operation.faults as fault] + + + + + + [/#list] + +
Faults
nametypeconditions
${fault.name}[@referenceDataType referenceType=fault.dataType/]${fault.conditions!" "}
+ [/#if] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processDataSyntax syntax] + [#-- @ftlvariable name="syntax" type="com.webcohesion.enunciate.api.datatype.Syntax" --] + [@file name=syntax.slug + ".html"] + [@boilerplate title=title + ": " + syntax.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : syntax.label , "href" : syntax.slug + ".html"} ]] +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + [@processDataType type=type/] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processDataType type] + [#-- @ftlvariable name="type" type="com.webcohesion.enunciate.api.datatype.DataType" --] + [#assign pagenav=[]/] + [#if type.values??] + [#list type.values as value] + [#assign pagenav=pagenav + [{ "href" : "#" + value.value, "title" : value.value }]/] + [/#list] + [/#if] + [@file name=type.slug + ".html"] + [@boilerplate title=title + ": " + type.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : type.syntax.label , "href" : type.syntax.slug + ".html"}, {"title" : type.label , "href" : type.slug + ".html"} ] pagenav=pagenav codeblocks=true] +

${type.label} Data Type

+ [#if type.deprecated??] + +
This data type has been deprecated. ${type.deprecated}
+ [/#if] + [#if type.description??] + +

${type.description}

+ [/#if] + +
+ [#if type.namespace.uri??] +
Namespace
+ [#if type.namespace.uri?length > 0] +
${type.namespace.uri}
+ [#else] +
(Default)
+ [/#if] + [/#if] + [#if type.namespace.schemaFile??] +
Schema
+
${type.namespace.schemaFile.href}
+ [/#if] + [#if type.since??] +
Available Since
+
${type.since}
+ [/#if] + [#if type.version??] +
Version
+
${type.version}
+ [/#if] + [#if type.abstract] +
Abstract Type
+
+ [/#if] + [#if type.subtypes??] +
Subtypes
+
[#list type.subtypes as subtype][#if subtype.slug??]${subtype.label}[#else]${subtype.label}[/#if][#if subtype_has_next], [/#if][/#list]
+ [/#if] + [#if type.interfaces??] +
Implemented Interfaces
+
[#list type.interfaces as iface]${iface.label}[#if iface_has_next], [/#if][/#list]
+ [/#if] + [#if type.seeAlso??] + [#list type.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [#if type.values??] + + + + + + + + + + + [#list type.values as value] + + + + + [/#list] + +
Values
valuedescription
${value.value}[#if value.since??]since ${value.since} [/#if]${value.description!" "}
+ [/#if] + [#if type.properties??] + + + + + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + + + [#list type.properties as property] + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + [/#list] + + [#if type.supertypes??] + [#list type.supertypes as supertype] + [#if supertype.value?? && supertype.value.properties?? && supertype.value.properties?size > 0] + + + + + [#list supertype.value.properties as superProperty] + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + [/#list] + + [/#if] + [/#list] + [/#if] +
Properties
namedata type${type.propertyMetadata[meta]}description
[@deprecation element=property]${property.name}[/@deprecation][@deprecation element=property][@referenceDataType referenceType=property.dataType/][/@deprecation][@deprecation element=property][@printPropertyMetadata property=property meta=meta/][/@deprecation][@deprecation element=property][#if property.since??]since ${property.since} [/#if]${property.description!" "}[/@deprecation]
Properties inherited from ${supertype.label}
${superProperty.name}[@referenceDataType referenceType=superProperty.dataType/][@printPropertyMetadata property=superProperty meta=meta/][#if superProperty.since??]since ${superProperty.since} [/#if]${superProperty.description!" "}
+ [/#if] + [#if type.example??] + +

Example

+ [#if type.abstract && type.subtypes?? ] + +
This data type is abstract. The example below may be incomplete. More accurate examples can be found in subtypes pages.
+ [/#if] + +
${type.example.body?xhtml}
+ [/#if] + [/@boilerplate] + [/@file] +[/#macro] +[#macro referenceDataType referenceType] +[#-- @ftlvariable name="type" type="com.webcohesion.enunciate.api.datatype.DataTypeReference" --] +[#if referenceType.containers??][#list referenceType.containers as container]${container?string} of [/#list][/#if][#if referenceType.slug??][/#if]${referenceType.label!"(custom)"}[#if referenceType.slug??][/#if] +[/#macro] +[#macro printPropertyMetadata property meta] + [#assign metaValue=property[meta]!({ "structure" : true })/] + [#if metaValue?is_hash && metaValue.structure!false] +[#if metaValue.href??][/#if][#if metaValue.title??][/#if]${metaValue.value!" "}[#if metaValue.title??][/#if][#if metaValue.href??][/#if] + [#else] +${metaValue} + [/#if] +[/#macro] \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/.gitignore b/src/main/webapp/WEB-INF/.gitignore new file mode 100644 index 0000000..a252987 --- /dev/null +++ b/src/main/webapp/WEB-INF/.gitignore @@ -0,0 +1,5 @@ +/LICENSE.md +/README.md +/changelog.xml +/gcube-app.xml +/CHANGELOG.md diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..ef9a39b --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,19 @@ + + + + + org.gcube.acme.ResourceInitializer + + + default + /docs/* + + + default + /api-docs/* + + + org.gcube.acme.ResourceInitializer + /* + + \ No newline at end of file diff --git a/src/main/webapp/docs/css/d4science_enunciate_custom.css b/src/main/webapp/docs/css/d4science_enunciate_custom.css new file mode 100644 index 0000000..9dae183 --- /dev/null +++ b/src/main/webapp/docs/css/d4science_enunciate_custom.css @@ -0,0 +1,25 @@ +.d4science_intro { + top: 0; + z-index: 2000; + position: fixed; + display: block ruby; + padding: 10px; + background: white; + width: 100%; +} + +.navbar-fixed-top { + top: 100px !important; +} + +.sidebar { + top: 160px !important; +} + +.navbar { + margin-bottom: 40px !important; +} + +.main { + top: 90px; +} \ No newline at end of file