From d211f7f879ae7b5ea4aa828b4780edc185598646 Mon Sep 17 00:00:00 2001 From: Massimiliano Assante Date: Thu, 25 Jun 2015 16:25:37 +0000 Subject: [PATCH] partially implemented invites handling, still to implement get methods for checking who was invited in a given environment git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portal/social-networking-library@115559 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 2 +- META-INF/KeySpace Structure.xlsx | Bin 41959 -> 43136 bytes pom.xml | 14 +- .../server/DBCassandraAstyanaxImpl.java | 216 +++++++++++++++++- .../server/DatabookCassandraTest.java | 177 +++++++++----- .../portal/databook/server/DatabookStore.java | 40 ++++ .../gcube/portal/databook/shared/Feed.java | 1 - .../gcube/portal/databook/shared/Invite.java | 144 ++++++++++++ .../shared/InviteOperationResult.java | 8 + .../portal/databook/shared/InviteStatus.java | 7 + .../shared/ex/InviteIDNotFoundException.java | 8 + .../ex/InviteStatusNotFoundException.java | 8 + 12 files changed, 552 insertions(+), 73 deletions(-) create mode 100644 src/main/java/org/gcube/portal/databook/shared/Invite.java create mode 100644 src/main/java/org/gcube/portal/databook/shared/InviteOperationResult.java create mode 100644 src/main/java/org/gcube/portal/databook/shared/InviteStatus.java create mode 100644 src/main/java/org/gcube/portal/databook/shared/ex/InviteIDNotFoundException.java create mode 100644 src/main/java/org/gcube/portal/databook/shared/ex/InviteStatusNotFoundException.java diff --git a/.classpath b/.classpath index 51be09f..61d6ab1 100644 --- a/.classpath +++ b/.classpath @@ -20,7 +20,7 @@ - + diff --git a/META-INF/KeySpace Structure.xlsx b/META-INF/KeySpace Structure.xlsx index bf6abfd9317a8c451bf011426964fd197fe1f929..fd706425b48d06cfe5e9d22efc2f808885b124bf 100644 GIT binary patch delta 34148 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 akJm9MGb5(JX+grR3|4{RqHpiaU;IA@hdGJ> delta 32975 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 TX!qS@W}3%CeYx74g}43>@y74L diff --git a/pom.xml b/pom.xml index d30a6d0..2e49aed 100644 --- a/pom.xml +++ b/pom.xml @@ -22,11 +22,11 @@ http://svn.d4science.research-infrastructures.eu/gcube/trunk/portal/${project.artifactId} - 2.5.1 + 2.7.0 distro - 1.6 - 1.6 + 1.7 + 1.7 UTF-8 UTF-8 @@ -59,6 +59,11 @@ com.google gwt-jsonmaker + + com.sun.mail + javax.mail + provided + com.liferay.portal portal-service @@ -73,11 +78,10 @@ junit 4.8 - com.google.gwt gwt-user - + ${gwtVersion} provided diff --git a/src/main/java/org/gcube/portal/databook/server/DBCassandraAstyanaxImpl.java b/src/main/java/org/gcube/portal/databook/server/DBCassandraAstyanaxImpl.java index 6a1e4f3..8de2a0e 100644 --- a/src/main/java/org/gcube/portal/databook/server/DBCassandraAstyanaxImpl.java +++ b/src/main/java/org/gcube/portal/databook/server/DBCassandraAstyanaxImpl.java @@ -10,10 +10,16 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + import org.apache.commons.lang.NullArgumentException; import org.gcube.portal.databook.shared.Comment; import org.gcube.portal.databook.shared.Feed; import org.gcube.portal.databook.shared.FeedType; +import org.gcube.portal.databook.shared.Invite; +import org.gcube.portal.databook.shared.InviteOperationResult; +import org.gcube.portal.databook.shared.InviteStatus; import org.gcube.portal.databook.shared.Like; import org.gcube.portal.databook.shared.Notification; import org.gcube.portal.databook.shared.NotificationChannelType; @@ -24,6 +30,8 @@ import org.gcube.portal.databook.shared.ex.ColumnNameNotFoundException; import org.gcube.portal.databook.shared.ex.CommentIDNotFoundException; import org.gcube.portal.databook.shared.ex.FeedIDNotFoundException; import org.gcube.portal.databook.shared.ex.FeedTypeNotFoundException; +import org.gcube.portal.databook.shared.ex.InviteIDNotFoundException; +import org.gcube.portal.databook.shared.ex.InviteStatusNotFoundException; import org.gcube.portal.databook.shared.ex.LikeIDNotFoundException; import org.gcube.portal.databook.shared.ex.NotificationChannelTypeNotFoundException; import org.gcube.portal.databook.shared.ex.NotificationIDNotFoundException; @@ -44,8 +52,6 @@ import com.netflix.astyanax.query.PreparedIndexExpression; import com.netflix.astyanax.serializers.StringSerializer; /** * @author Massimiliano Assante ISTI-CNR - * @version 1.0 Dec 2012 - * * * This class is used for querying and adding data to Cassandra via Astyanax High Level API * @@ -65,6 +71,7 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { public static final String FEEDS = "Feeds"; public static final String COMMENTS = "Comments"; public static final String LIKES = "Likes"; + public static final String INVITES = "Invites"; public static final String VRE_TIMELINE_FEEDS = "VRETimeline"; public static final String USER_TIMELINE_FEEDS = "USERTimeline"; public static final String APP_TIMELINE_FEEDS = "AppTimeline"; @@ -74,6 +81,8 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { 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_FEEDS = "HashtaggedFeeds"; // contains hashtags per type associated with vre and feed + 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 private static ColumnFamily cf_Connections = new ColumnFamily( @@ -111,6 +120,10 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { LIKES, // Column Family Name StringSerializer.get(), // Key Serializer StringSerializer.get()); // Column Serializer + private static ColumnFamily cf_Invites = new ColumnFamily( + INVITES, // Column Family Name + StringSerializer.get(), // Key Serializer + StringSerializer.get()); // Column Serializer private static ColumnFamily cf_UserLikedFeeds = new ColumnFamily( USER_LIKED_FEEDS, // Column Family Name StringSerializer.get(), // Key Serializer @@ -140,6 +153,15 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { StringSerializer.get(), // Key Serializer StringSerializer.get()); // Column Serializer + private static ColumnFamily cf_VREInvites = new ColumnFamily( + VRE_INVITES, // Column Family Name + StringSerializer.get(), // Key Serializer + StringSerializer.get()); // Column Serializer + protected static ColumnFamily cf_EmailInvites = new ColumnFamily( + EMAIL_INVITES, // Column Family Name + StringSerializer.get(), // Key Serializer + StringSerializer.get()); // Column Serializer + /** @@ -1528,6 +1550,157 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { toReturn = getFeedsByIds(feedIds); return toReturn; } + /* + * + ********************** Invites *********************** + * + */ + /** + * common part to save a feed + * @param feed + * @return the partial mutation batch instance + */ + private MutationBatch initSaveInvite(Invite invite) { + if (invite == null) + throw new NullArgumentException("Invite instance is null"); + // Inserting data + MutationBatch m = conn.getKeyspace().prepareMutationBatch(); + //an entry in the feed CF + m.withRow(cf_Invites, invite.getKey().toString()) + .putColumn("SenderUserId", invite.getSenderUserId(), null) + .putColumn("Vreid", invite.getVreid(), null) + .putColumn("InvitedEmail", invite.getInvitedEmail(), null) + .putColumn("ControlCode", invite.getControlCode(), null) + .putColumn("Status", invite.getStatus().toString(), null) + .putColumn("Time", invite.getTime().getTime()+"", null) + .putColumn("SenderFullName", invite.getSenderFullName(), null); + return m; + } + + /** + * {@inheritDoc} + */ + @Override + public String isExistingInvite(String vreid, String email) { + OperationResult> result = null; + try { + result = conn.getKeyspace().prepareQuery(cf_EmailInvites) + .getKeySlice(email) + .execute(); + } catch (ConnectionException e) { + e.printStackTrace(); + } + // Iterate rows and their columns + for (Row row : result.getResult()) { + for (Column column : row.getColumns()) { + if (column.getName().compareTo(vreid)==0) + return column.getStringValue(); + } + } + return null; + } + /** + * {@inheritDoc} + */ + @Override + public InviteOperationResult saveInvite(Invite invite) throws AddressException, NullArgumentException { + 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 ..."); + + MutationBatch m = initSaveInvite(invite); + //an entry in the VRE Invites + m.withRow(cf_VREInvites, invite.getVreid()) + .putColumn(invite.getKey().toString(), InviteStatus.PENDING.toString(), null); + + //an entry in the EMAIL Invites + m.withRow(cf_EmailInvites, email) + .putColumn(invite.getVreid(), invite.getKey().toString(), null); + boolean result = execute(m); + return result ? InviteOperationResult.SUCCESS : InviteOperationResult.FAILED; + } + /** + * {@inheritDoc} + */ + @Override + public Invite readInvite(String inviteid) throws InviteIDNotFoundException, InviteStatusNotFoundException { + Invite toReturn = new Invite(); + OperationResult> result; + try { + result = conn.getKeyspace().prepareQuery(cf_Invites) + .getKey(inviteid) + .execute(); + + ColumnList columns = result.getResult(); + if (columns.size() == 0) { + throw new InviteStatusNotFoundException("The requested inviteid: " + inviteid + " is not existing"); + } + + toReturn.setKey(inviteid); + toReturn.setSenderUserId(columns.getColumnByName("SenderUserId").getStringValue()); + toReturn.setVreid(columns.getColumnByName("Vreid").getStringValue()); + toReturn.setInvitedEmail(columns.getColumnByName("InvitedEmail").getStringValue()); + toReturn.setControlCode(columns.getColumnByName("ControlCode").getStringValue()); + InviteStatus status = getInviteStatusType(columns.getColumnByName("Status").getStringValue()); + toReturn.setStatus(status); + toReturn.setTime(getDateFromTimeInMillis(columns.getColumnByName("Time").getStringValue())); + toReturn.setSenderFullName(columns.getColumnByName("SenderFullName").getStringValue()); + + } catch (ConnectionException e) { + e.printStackTrace(); + return null; + } + 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"); + + MutationBatch m = conn.getKeyspace().prepareMutationBatch(); + //update in the Invites Static CF + m.withRow(cf_Invites, inviteid).putColumn("Status", status.toString(), null); + //updated in the VREInvites Dynamic CF + m.withRow(cf_VREInvites, toSet.getVreid()).putColumn(inviteid, status.toString(), null); + try { + m.execute(); + } catch (ConnectionException 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 getAllInvitedEmailsByVRE(String vreid) { + // TODO Auto-generated method stub + return null; + } + /** + * {@inheritDoc} + */ + @Override + public List getPendingInvitedEmailsByVRE(String vreid) { + // TODO Auto-generated method stub + return null; + } /** * {@inheritDoc} */ @@ -1609,6 +1782,26 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { else throw new FeedTypeNotFoundException("The Feed Type was not recognized should be one of " + FeedType.values() + " asked for: " + type); } + + /** + * simply return an enum representing the invite status type + * @param type . + * @return correct enum representing the feed type + * @throws TypeNotFoundException . + */ + private InviteStatus getInviteStatusType(String type) throws InviteStatusNotFoundException { + if (type.compareTo("PENDING") == 0) { + return InviteStatus.PENDING; + } + else if (type.compareTo("ACCEPTED") == 0) { + return InviteStatus.ACCEPTED; + } + else if (type.compareTo("REJECTED") == 0) { + return InviteStatus.REJECTED; + } + else + throw new InviteStatusNotFoundException("The Invite Status was not recognized should be one of " + InviteStatus.values() + " asked for: " + type); + } /** * simply return an enum representing the feed type @@ -1835,7 +2028,24 @@ public final class DBCassandraAstyanaxImpl implements DatabookStore { _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; + } + + diff --git a/src/main/java/org/gcube/portal/databook/server/DatabookCassandraTest.java b/src/main/java/org/gcube/portal/databook/server/DatabookCassandraTest.java index 21bd051..b1a0f00 100644 --- a/src/main/java/org/gcube/portal/databook/server/DatabookCassandraTest.java +++ b/src/main/java/org/gcube/portal/databook/server/DatabookCassandraTest.java @@ -10,8 +10,13 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import javax.mail.internet.AddressException; + import org.gcube.portal.databook.shared.Feed; import org.gcube.portal.databook.shared.FeedType; +import org.gcube.portal.databook.shared.Invite; +import org.gcube.portal.databook.shared.InviteOperationResult; +import org.gcube.portal.databook.shared.InviteStatus; import org.gcube.portal.databook.shared.NotificationChannelType; import org.gcube.portal.databook.shared.NotificationType; import org.gcube.portal.databook.shared.PrivacyLevel; @@ -23,13 +28,16 @@ import org.junit.BeforeClass; import org.junit.Test; import com.google.common.collect.ImmutableMap; +import com.google.gwt.thirdparty.javascript.jscomp.Result; import com.netflix.astyanax.connectionpool.OperationResult; import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; +import com.netflix.astyanax.ddl.ColumnDefinition; import com.netflix.astyanax.ddl.ColumnFamilyDefinition; import com.netflix.astyanax.model.ColumnFamily; import com.netflix.astyanax.model.Row; import com.netflix.astyanax.model.Rows; import com.netflix.astyanax.serializers.StringSerializer; +import com.netflix.astyanax.thrift.ddl.ThriftColumnDefinitionImpl; import com.netflix.astyanax.thrift.ddl.ThriftColumnFamilyDefinitionImpl; public class DatabookCassandraTest { @@ -45,74 +53,117 @@ public class DatabookCassandraTest { store.closeConnection(); System.out.println("End"); } - - + @Test - public void testHashTag() { - try { - final String VREID = "/gcube/devsec/devVRE"; - final String HASHTAG1 = "#testHashTag"; - final String HASHTAG2 = "#testHashTag3"; - List hashtags = new LinkedList(); - hashtags.add(HASHTAG1); - hashtags.add(HASHTAG2); - -// Feed feed = new Feed(UUID.randomUUID().toString(), FeedType.TWEET, "massimiliano.assante", new Date(), VREID, -// "www.d4science.org/monitor", "thumbUri", "This is a feed with " + HASHTAG1 + " and " + HASHTAG2, PrivacyLevel.VRES, "Massimiliano Assante", "massimiliano.assante@isti.cnr.it", "thumburl", "linkTitle", "linkDesc", "host"); -// assertTrue(store.saveUserFeed(feed)); -// assertTrue(store.saveHashTags(feed.getKey(), VREID, hashtags)); -// assertTrue(store.deleteHashTags("d0c64e42-9616-4e24-a65a-7a63a280d676", VREID, hashtags)); -// System.out.println(feed); -// - System.out.println("\ngetting getVREHashtagsWithOccurrence for " + VREID); - Map hashtagsWithOcc = store.getVREHashtagsWithOccurrence(VREID); - for (String hashtag : hashtagsWithOcc.keySet()) { - System.out.println(hashtag + ":" + hashtagsWithOcc.get(hashtag)); - } - - System.out.println("\ngetting getVREFeedsByHashtag for " + VREID + " and " + HASHTAG1); - for (Feed theFeed : store.getVREFeedsByHashtag(VREID, HASHTAG1)) { - System.out.println(theFeed); - } - + public void testInvites() { + String controlCode = UUID.randomUUID().toString(); + String vreid = "/gcube/devsec/devVRE"; + String email = "m.assante@gmail.com"; + Invite invite = new Invite(UUID.randomUUID().toString(), "massimiliano.assante", vreid, email, controlCode, InviteStatus.PENDING, new Date(), "Massimiliano Assante"); + try { + InviteOperationResult result = store.saveInvite(invite); + System.out.println(result); + String inviteid = store.isExistingInvite(vreid, email); + System.out.println(store.readInvite(inviteid)); + store.setInviteStatus(vreid, email, InviteStatus.ACCEPTED); + System.out.println(store.readInvite(inviteid)); } catch (Exception e) { e.printStackTrace(); } - - + } -// /** -// * use exclusively to add a new (Dynamic) CF to a keyspace -// */ -// @Test -// public void addHashtagsColumnFamilies() { -// ColumnFamily cf_HashtagsCounter = new ColumnFamily( -// DBCassandraAstyanaxImpl.HASHTAGS_COUNTER, // Column Family Name -// StringSerializer.get(), // Key Serializer -// StringSerializer.get()); // Column Serializer -// ColumnFamily cf_HashtagTimeline = new ColumnFamily( -// DBCassandraAstyanaxImpl.HASHTAGGED_FEEDS, // Column Family Name -// StringSerializer.get(), // Key Serializer -// StringSerializer.get()); // Column Serializer -// -// try { -// new CassandraClusterConnection(false).getKeyspace().createColumnFamily(cf_HashtagsCounter, ImmutableMap.builder() -// .put("default_validation_class", "UTF8Type") -// .put("key_validation_class", "UTF8Type") -// .put("comparator_type", "UTF8Type") -// .build()); -// -// new CassandraClusterConnection(false).getKeyspace().createColumnFamily(cf_HashtagTimeline, ImmutableMap.builder() -// .put("default_validation_class", "UTF8Type") -// .put("key_validation_class", "UTF8Type") -// .put("comparator_type", "UTF8Type") -// .build()); -// -// } catch (ConnectionException e) { -// e.printStackTrace(); -// } -// } - + + // @Test + // public void testHashTag() { + // try { + // final String VREID = "/gcube/devsec/devVRE"; + // final String HASHTAG1 = "#testHashTag"; + // final String HASHTAG2 = "#testHashTag3"; + // List hashtags = new LinkedList(); + // hashtags.add(HASHTAG1); + // hashtags.add(HASHTAG2); + // + //// Feed feed = new Feed(UUID.randomUUID().toString(), FeedType.TWEET, "massimiliano.assante", new Date(), VREID, + //// "www.d4science.org/monitor", "thumbUri", "This is a feed with " + HASHTAG1 + " and " + HASHTAG2, PrivacyLevel.VRES, "Massimiliano Assante", "massimiliano.assante@isti.cnr.it", "thumburl", "linkTitle", "linkDesc", "host"); + //// assertTrue(store.saveUserFeed(feed)); + //// assertTrue(store.saveHashTags(feed.getKey(), VREID, hashtags)); + //// assertTrue(store.deleteHashTags("d0c64e42-9616-4e24-a65a-7a63a280d676", VREID, hashtags)); + //// System.out.println(feed); + //// + // System.out.println("\ngetting getVREHashtagsWithOccurrence for " + VREID); + // Map hashtagsWithOcc = store.getVREHashtagsWithOccurrence(VREID); + // for (String hashtag : hashtagsWithOcc.keySet()) { + // System.out.println(hashtag + ":" + hashtagsWithOcc.get(hashtag)); + // } + // + // System.out.println("\ngetting getVREFeedsByHashtag for " + VREID + " and " + HASHTAG1); + // for (Feed theFeed : store.getVREFeedsByHashtag(VREID, HASHTAG1)) { + // System.out.println(theFeed); + // } + // + // } catch (Exception e) { + // e.printStackTrace(); + // } + // + // + // } + + + // /** + // * use exclusively to add a new (Static) CF to a keyspace + // */ + // @Test + // public void addInvitesStaticColumnFamilies() { + // ColumnFamily CF_INVITES = ColumnFamily.newColumnFamily(DBCassandraAstyanaxImpl.INVITES, StringSerializer.get(), StringSerializer.get()); + // + // try { + // new CassandraClusterConnection(false).getKeyspace().createColumnFamily(CF_INVITES, ImmutableMap.builder() + // .put("default_validation_class", "UTF8Type") + // .put("key_validation_class", "UTF8Type") + // .put("comparator_type", "UTF8Type") + // .build()); + // + // } catch (ConnectionException e) { + // e.printStackTrace(); + // } + // System.out.println("addInvitesStaticColumnFamily END"); + // } + + + // /** + // * use exclusively to add a new (Dynamic) CF to a keyspace + // */ + // @Test + // public void addInvitesDynamicColumnFamilies() { + // System.out.println("addInvitesColumnFamilies"); + // ColumnFamily cf_HashtagsCounter = new ColumnFamily( + // DBCassandraAstyanaxImpl.VRE_INVITES, // Column Family Name + // StringSerializer.get(), // Key Serializer + // StringSerializer.get()); // Column Serializer + // ColumnFamily cf_HashtagTimeline = new ColumnFamily( + // DBCassandraAstyanaxImpl.EMAIL_INVITES, // Column Family Name + // StringSerializer.get(), // Key Serializer + // StringSerializer.get()); // Column Serializer + // + // try { + // new CassandraClusterConnection(false).getKeyspace().createColumnFamily(cf_HashtagsCounter, ImmutableMap.builder() + // .put("default_validation_class", "UTF8Type") + // .put("key_validation_class", "UTF8Type") + // .put("comparator_type", "UTF8Type") + // .build()); + // + // new CassandraClusterConnection(false).getKeyspace().createColumnFamily(cf_HashtagTimeline, ImmutableMap.builder() + // .put("default_validation_class", "UTF8Type") + // .put("key_validation_class", "UTF8Type") + // .put("comparator_type", "UTF8Type") + // .build()); + // + // } catch (ConnectionException e) { + // e.printStackTrace(); + // } + // System.out.println("addInvitesColumnFamilies END"); + // } + // private List getKeys() { // List toReturn = new ArrayList(); diff --git a/src/main/java/org/gcube/portal/databook/server/DatabookStore.java b/src/main/java/org/gcube/portal/databook/server/DatabookStore.java index 71c3762..e7c5175 100644 --- a/src/main/java/org/gcube/portal/databook/server/DatabookStore.java +++ b/src/main/java/org/gcube/portal/databook/server/DatabookStore.java @@ -3,8 +3,14 @@ package org.gcube.portal.databook.server; import java.util.List; import java.util.Map; +import javax.mail.internet.AddressException; + +import org.apache.commons.lang.NullArgumentException; import org.gcube.portal.databook.shared.Comment; import org.gcube.portal.databook.shared.Feed; +import org.gcube.portal.databook.shared.Invite; +import org.gcube.portal.databook.shared.InviteOperationResult; +import org.gcube.portal.databook.shared.InviteStatus; import org.gcube.portal.databook.shared.Like; import org.gcube.portal.databook.shared.Notification; import org.gcube.portal.databook.shared.NotificationChannelType; @@ -14,6 +20,8 @@ import org.gcube.portal.databook.shared.ex.ColumnNameNotFoundException; import org.gcube.portal.databook.shared.ex.CommentIDNotFoundException; import org.gcube.portal.databook.shared.ex.FeedIDNotFoundException; import org.gcube.portal.databook.shared.ex.FeedTypeNotFoundException; +import org.gcube.portal.databook.shared.ex.InviteIDNotFoundException; +import org.gcube.portal.databook.shared.ex.InviteStatusNotFoundException; import org.gcube.portal.databook.shared.ex.LikeIDNotFoundException; import org.gcube.portal.databook.shared.ex.NotificationChannelTypeNotFoundException; import org.gcube.portal.databook.shared.ex.NotificationIDNotFoundException; @@ -352,6 +360,38 @@ public interface DatabookStore { * @return all the feeds having the hashtag passed as parameter */ List getVREFeedsByHashtag(String vreid, String hashtag) throws PrivacyLevelTypeNotFoundException, FeedTypeNotFoundException, FeedIDNotFoundException, 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, NullArgumentException; + /** + * + * @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 InviteIDNotFoundException + */ + 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; + /** + * return the list of invites + * @param vreid + * @return + */ + List getAllInvitedEmailsByVRE(String vreid); + + List getPendingInvitedEmailsByVRE(String vreid); /** * close the connection to the underlying database */ diff --git a/src/main/java/org/gcube/portal/databook/shared/Feed.java b/src/main/java/org/gcube/portal/databook/shared/Feed.java index cd13adb..52aaefb 100644 --- a/src/main/java/org/gcube/portal/databook/shared/Feed.java +++ b/src/main/java/org/gcube/portal/databook/shared/Feed.java @@ -6,7 +6,6 @@ import java.util.Date; /** * * @author Massimiliano Assante, ISTI-CNR - * @version 0.1 July 2012 * */ @SuppressWarnings("serial") diff --git a/src/main/java/org/gcube/portal/databook/shared/Invite.java b/src/main/java/org/gcube/portal/databook/shared/Invite.java new file mode 100644 index 0000000..be1548d --- /dev/null +++ b/src/main/java/org/gcube/portal/databook/shared/Invite.java @@ -0,0 +1,144 @@ +package org.gcube.portal.databook.shared; + +import java.io.Serializable; +import java.util.Date; +/** + * + * @author Massimiliano Assante, ISTI-CNR + * + */ +@SuppressWarnings("serial") +public class Invite implements Serializable { + + private String key; + private String senderUserId; + private String vreid; + private String invitedEmail; + private String controlCode; + private InviteStatus status; + private Date time; + private String senderFullName; + + + public Invite() { + super(); + } + + + + + public Invite(String key, String senderUserId, String vreid, + String invitedEmail, String controlCode, InviteStatus status, + Date time, String senderFullName) { + super(); + this.key = key; + this.senderUserId = senderUserId; + this.vreid = vreid; + this.invitedEmail = invitedEmail; + this.controlCode = controlCode; + this.status = status; + this.time = time; + this.senderFullName = senderFullName; + } + + + + + public String getKey() { + return key; + } + + + + + public void setKey(String key) { + this.key = key; + } + + + + + public String getSenderUserId() { + return senderUserId; + } + + + public void setSenderUserId(String senderUserId) { + this.senderUserId = senderUserId; + } + + + public String getVreid() { + return vreid; + } + + + public void setVreid(String vreid) { + this.vreid = vreid; + } + + + public String getInvitedEmail() { + return invitedEmail; + } + + + public void setInvitedEmail(String invitedEmail) { + this.invitedEmail = invitedEmail; + } + + + public String getControlCode() { + return controlCode; + } + + + public void setControlCode(String controlCode) { + this.controlCode = controlCode; + } + + + public InviteStatus getStatus() { + return status; + } + + + public void setStatus(InviteStatus status) { + this.status = status; + } + + + public Date getTime() { + return time; + } + + + public void setTime(Date time) { + this.time = time; + } + + + public String getSenderFullName() { + return senderFullName; + } + + + public void setSenderFullName(String senderFullName) { + this.senderFullName = senderFullName; + } + + + + + @Override + public String toString() { + return "Invite [key=" + key + ", senderUserId=" + senderUserId + + ", vreid=" + vreid + ", invitedEmail=" + invitedEmail + + ", controlCode=" + controlCode + ", status=" + status + + ", time=" + time + ", senderFullName=" + senderFullName + "]"; + } + + + + +} diff --git a/src/main/java/org/gcube/portal/databook/shared/InviteOperationResult.java b/src/main/java/org/gcube/portal/databook/shared/InviteOperationResult.java new file mode 100644 index 0000000..da0327f --- /dev/null +++ b/src/main/java/org/gcube/portal/databook/shared/InviteOperationResult.java @@ -0,0 +1,8 @@ +package org.gcube.portal.databook.shared; + +public enum InviteOperationResult { + SUCCESS, + FAILED, + //If I send an invite the same email in the same environment more than once + ALREADY_INVITED; +} diff --git a/src/main/java/org/gcube/portal/databook/shared/InviteStatus.java b/src/main/java/org/gcube/portal/databook/shared/InviteStatus.java new file mode 100644 index 0000000..b3b5b90 --- /dev/null +++ b/src/main/java/org/gcube/portal/databook/shared/InviteStatus.java @@ -0,0 +1,7 @@ +package org.gcube.portal.databook.shared; + +public enum InviteStatus { + PENDING, + ACCEPTED, + REJECTED; +} diff --git a/src/main/java/org/gcube/portal/databook/shared/ex/InviteIDNotFoundException.java b/src/main/java/org/gcube/portal/databook/shared/ex/InviteIDNotFoundException.java new file mode 100644 index 0000000..a7d5cb0 --- /dev/null +++ b/src/main/java/org/gcube/portal/databook/shared/ex/InviteIDNotFoundException.java @@ -0,0 +1,8 @@ +package org.gcube.portal.databook.shared.ex; + +@SuppressWarnings("serial") +public class InviteIDNotFoundException extends Exception { + public InviteIDNotFoundException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/org/gcube/portal/databook/shared/ex/InviteStatusNotFoundException.java b/src/main/java/org/gcube/portal/databook/shared/ex/InviteStatusNotFoundException.java new file mode 100644 index 0000000..5f4f9c2 --- /dev/null +++ b/src/main/java/org/gcube/portal/databook/shared/ex/InviteStatusNotFoundException.java @@ -0,0 +1,8 @@ +package org.gcube.portal.databook.shared.ex; + +@SuppressWarnings("serial") +public class InviteStatusNotFoundException extends Exception { + public InviteStatusNotFoundException(String message) { + super(message); + } +}