Enterprise Social Messaging Environment (ESME)
  1. Enterprise Social Messaging Environment (ESME)
  2. ESME-292

Implement API2 methods for Tag/Conversation follow/unfollow

    Details

    • Type: New Feature New Feature
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: Release 1.1
    • Fix Version/s: 1.3
    • Component/s: API
    • Labels:
      None

      Description

      Implement API methods for follow and unfollow of conversations and tags. Should be implemented in REST style, so methods should look like:

      GET api2/conversations/CONVERSATIONID/followers
      POST api2/conversations/CONVERSATIONID/followers (with user ID that will follow)
      DELETE api2/conversations/CONVERSATIONID/followers/USERID

      GET api2/tags/TAGNAME/followers
      POST api2/tags/TAGNAME/followers (with user ID that will follow)
      DELETE api2/tags/TAGNAME/followers/USERID

        Activity

        Ethan Jewett created issue -
        Dick Hirsch made changes -
        Field Original Value New Value
        Fix Version/s 1.3 [ 12316109 ]
        Fix Version/s 1.2 [ 12315284 ]
        Hide
        Dick Hirsch added a comment -

        I looked at this and copied together code from other Scala files

        Follow conversation

        val cid: String = S.param("cid")
        val messages = Message.findMessages(List(cid))
        val user = User.currentUser
        m = messages.values.toList.head
        if (!m.followers.contains(u)) {
        m.followers += u
        m.save
        )

        unFollow conversation

        val cid: String = S.param("cid")
        val messages = Message.findMessages(List(cid))
        val user = User.currentUser
        m = messages.values.toList.head
        if (m.followers.contains(u)) {
        m.followers -= u
        m.save
        )

        Show
        Dick Hirsch added a comment - I looked at this and copied together code from other Scala files Follow conversation val cid: String = S.param("cid") val messages = Message.findMessages(List(cid)) val user = User.currentUser m = messages.values.toList.head if (!m.followers.contains(u)) { m.followers += u m.save ) unFollow conversation val cid: String = S.param("cid") val messages = Message.findMessages(List(cid)) val user = User.currentUser m = messages.values.toList.head if (m.followers.contains(u)) { m.followers -= u m.save )
        Hide
        Ethan Jewett added a comment -

        Looks like a good start. Do you want to take a shot at it?

        If you want to work on it together, just create a branch for this issue. Then you can commit partially working code and we can solve problems together.

        Show
        Ethan Jewett added a comment - Looks like a good start. Do you want to take a shot at it? If you want to work on it together, just create a branch for this issue. Then you can commit partially working code and we can solve problems together.
        Hide
        Dick Hirsch added a comment -

        Just checked in some code in the branch - please take a look

        Show
        Dick Hirsch added a comment - Just checked in some code in the branch - please take a look
        Hide
        Dick Hirsch added a comment -

        Made more changes ...

        Now just

        [WARNING] D:\apache\esme\branches\api2-1.3\server\src\main\scala\org\apache\esme
        \api\API2.scala:767: error: value isEmpty is not a member of org.apache.esme.mod
        el.Tag
        [WARNING] if(tag.isEmpty)

        Show
        Dick Hirsch added a comment - Made more changes ... Now just [WARNING] D:\apache\esme\branches\api2-1.3\server\src\main\scala\org\apache\esme \api\API2.scala:767: error: value isEmpty is not a member of org.apache.esme.mod el.Tag [WARNING] if(tag.isEmpty)
        Hide
        Ethan Jewett added a comment -

        Does the Tag class have a method isEmpty? I think it used to be an Option[Tag], which would have that method, but it no longer is.

        Show
        Ethan Jewett added a comment - Does the Tag class have a method isEmpty? I think it used to be an Option [Tag] , which would have that method, but it no longer is.
        Hide
        Dick Hirsch added a comment - - edited

        What could we use instead of this method?

        I looked at other API2 methods and some don't even deal with the problem at all. They just return a 200.

        Show
        Dick Hirsch added a comment - - edited What could we use instead of this method? I looked at other API2 methods and some don't even deal with the problem at all. They just return a 200.
        Hide
        Ethan Jewett added a comment -

        I think the key question is what happens in this line in the two cases where the search (a) returns a list of tags and (b) returns an empty list.

        val tag = Tag.findAll(By(Tag.name, tagId.openOr(""))).head

        I think that if you want to avoid Option, you could store the returned list (rather than the head) and do your tests based on the length of the list.

        Show
        Ethan Jewett added a comment - I think the key question is what happens in this line in the two cases where the search (a) returns a list of tags and (b) returns an empty list. val tag = Tag.findAll(By(Tag.name, tagId.openOr(""))).head I think that if you want to avoid Option, you could store the returned list (rather than the head) and do your tests based on the length of the list.
        Hide
        Dick Hirsch added a comment -

        Would that just be:

        val tag = Tag.findAll(By(Tag.name, tagId.openOr("")))

        Or could you still check the length of tag instead of using isEmpty if the head is returned?

        Show
        Dick Hirsch added a comment - Would that just be: val tag = Tag.findAll(By(Tag.name, tagId.openOr(""))) Or could you still check the length of tag instead of using isEmpty if the head is returned?
        Hide
        Ethan Jewett added a comment -

        Yes, now tag will be a list, I believe. So you could do tag.length to get the number of items in the list (or you could still use tag.isEmpty because lists support that method as well). Or you could do tag.head to get the first element of the list.

        Show
        Ethan Jewett added a comment - Yes, now tag will be a list, I believe. So you could do tag.length to get the number of items in the list (or you could still use tag.isEmpty because lists support that method as well). Or you could do tag.head to get the first element of the list.
        Hide
        Dick Hirsch added a comment -

        The problem with this is that tag is a List then you you have other problems in that tag is no longer a Tag object.

        if (!tag.followers.contains(user))

        { tag.followers += user tag.save }

        I think we have to keep the .head and then find a replacement for the .isEmpty check. Or delete it entirely and just always return the HTTP Return Code 200

        Show
        Dick Hirsch added a comment - The problem with this is that tag is a List then you you have other problems in that tag is no longer a Tag object. if (!tag.followers.contains(user)) { tag.followers += user tag.save } I think we have to keep the .head and then find a replacement for the .isEmpty check. Or delete it entirely and just always return the HTTP Return Code 200
        Hide
        Ethan Jewett added a comment -

        Try this:

        val tagList = Tag.findAll(By(Tag.name, tagId.openOr("")))
        val tag = tagList.head

        Then you have both the list (and can use isEmpty on it) and the Tag available to you

        Show
        Ethan Jewett added a comment - Try this: val tagList = Tag.findAll(By(Tag.name, tagId.openOr(""))) val tag = tagList.head Then you have both the list (and can use isEmpty on it) and the Tag available to you
        Hide
        Dick Hirsch added a comment -

        OK - tried this and it worked - I'll do a check-in on Monday - who wants to test this stuff ;->

        Show
        Dick Hirsch added a comment - OK - tried this and it worked - I'll do a check-in on Monday - who wants to test this stuff ;->
        Hide
        Dick Hirsch added a comment -

        finished add / deletes for tags / conversations

        Now to look at the gets

        Would be something like this:

        val ret: Box[Tuple3[Int,Map[String,String],Box[Elem]]] =
        for (user <- User.currentUser)
        yield {
        val tagList = Tag.findAll(By(Tag.name, tagId.openOr(""))).flatMap(_.toXml)

        if(tagList.length == 0)
        (404,Map(),Empty)
        else
        (200,Map(),Full(<tags>

        {tagList}

        </tags>))
        }

        Show
        Dick Hirsch added a comment - finished add / deletes for tags / conversations Now to look at the gets Would be something like this: val ret: Box[Tuple3[Int,Map [String,String] ,Box [Elem] ]] = for (user <- User.currentUser) yield { val tagList = Tag.findAll(By(Tag.name, tagId.openOr(""))).flatMap(_.toXml) if(tagList.length == 0) (404,Map(),Empty) else (200,Map(),Full(<tags> {tagList} </tags>)) }
        Dick Hirsch made changes -
        Assignee Dick Hirsch [ rhirsch ]
        Dick Hirsch made changes -
        Status Open [ 1 ] In Progress [ 3 ]
        Hide
        Dick Hirsch added a comment -

        I have a question regarding:

        val tagList = Tag.findAll(By(Tag.name, tagId.openOr(""))).flatMap(_.toXml)

        This call isn't going to work, because it doesn't query the tag's followers - what would the correct query be? Something like this:

        val tagList = Tag.findAll(By(Tag.name, followers(????.user (""))).flatMap(_.toXml)

        Show
        Dick Hirsch added a comment - I have a question regarding: val tagList = Tag.findAll(By(Tag.name, tagId.openOr(""))).flatMap(_.toXml) This call isn't going to work, because it doesn't query the tag's followers - what would the correct query be? Something like this: val tagList = Tag.findAll(By(Tag.name, followers(????.user (""))).flatMap(_.toXml)
        Hide
        Ethan Jewett added a comment -

        I think this line stays the same. We would only do stuff with the followers later in the logic. You are provided with the tag name as part of the query, so use that to find the tag, then once the tag is found you can add/remove/display the tag's followers.

        Show
        Ethan Jewett added a comment - I think this line stays the same. We would only do stuff with the followers later in the logic. You are provided with the tag name as part of the query, so use that to find the tag, then once the tag is found you can add/remove/display the tag's followers.
        Hide
        Dick Hirsch added a comment - - edited

        Was the intention that these REST calls return all users who follow the particular tag / conversation. Do we really want to make this data publicly available?. Isn't this is a bit of a privacy issue? This functionality isn't available via the UI either.

        Show
        Dick Hirsch added a comment - - edited Was the intention that these REST calls return all users who follow the particular tag / conversation. Do we really want to make this data publicly available?. Isn't this is a bit of a privacy issue? This functionality isn't available via the UI either.
        Hide
        Ethan Jewett added a comment -

        Good point. That would be a huge privacy issue. Maybe we should drop the GET method entirely for the moment?

        Show
        Ethan Jewett added a comment - Good point. That would be a huge privacy issue. Maybe we should drop the GET method entirely for the moment?
        Hide
        Dick Hirsch added a comment -

        OK - I'll drop the Gets. Maybe, we can discuss this at later date.

        Show
        Dick Hirsch added a comment - OK - I'll drop the Gets. Maybe, we can discuss this at later date.
        Hide
        Dick Hirsch added a comment -

        I'll merge the changes from the branch back into the trunk and then close this item

        Show
        Dick Hirsch added a comment - I'll merge the changes from the branch back into the trunk and then close this item
        Hide
        Dick Hirsch added a comment - - edited

        Finally had a chance to test these functions and there is a problem with the following/unfollowing of conversations:

        id <- conversationId.map(toLong))
        yield {
        val messages = Message.findMessages(List(id))

        The findMessage calls is usually based on msgid but we just have ConversationId. How can we search by conversationID? Do we need a new findMessages call that searches by ConvID and that still takes into account the groups?

        This is just a problem when a user uses a conversationid which doesn't exist

        Show
        Dick Hirsch added a comment - - edited Finally had a chance to test these functions and there is a problem with the following/unfollowing of conversations: id <- conversationId.map(toLong)) yield { val messages = Message.findMessages(List(id)) The findMessage calls is usually based on msgid but we just have ConversationId. How can we search by conversationID? Do we need a new findMessages call that searches by ConvID and that still takes into account the groups? This is just a problem when a user uses a conversationid which doesn't exist
        Hide
        Ethan Jewett added a comment -

        It's a little confusing, but the conversation ID actually is the message ID of the first message in the conversation. So I think this does do what you might expect and find the first message in the conversation.

        Show
        Ethan Jewett added a comment - It's a little confusing, but the conversation ID actually is the message ID of the first message in the conversation. So I think this does do what you might expect and find the first message in the conversation.
        Hide
        Hirsch, Richard added a comment -

        OK - a problem occurs when the REST call references a conversationID that doesn't exist but a msgID with this id does exist.

        Show
        Hirsch, Richard added a comment - OK - a problem occurs when the REST call references a conversationID that doesn't exist but a msgID with this id does exist.
        Hide
        Ethan Jewett added a comment -

        Next step is to write tests for this stuff so that we can have a repeatable version of the problem I take it you mean that the problem is since this message ID is not a conversation, then the user ends up following a non-existant conversation so they never get updates from it? We should probably trap this situation and return an HTTP error. Or is there some sort of technical problem?

        Show
        Ethan Jewett added a comment - Next step is to write tests for this stuff so that we can have a repeatable version of the problem I take it you mean that the problem is since this message ID is not a conversation, then the user ends up following a non-existant conversation so they never get updates from it? We should probably trap this situation and return an HTTP error. Or is there some sort of technical problem?
        Hide
        Hudson added a comment -

        Integrated in ESME #660 (See https://builds.apache.org/job/ESME/660/)
        ESME-292 Implement API2 methods for Tag/Conversation follow/unfollow

        rhirsch :
        Files :

        • /esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala
        Show
        Hudson added a comment - Integrated in ESME #660 (See https://builds.apache.org/job/ESME/660/ ) ESME-292 Implement API2 methods for Tag/Conversation follow/unfollow rhirsch : Files : /esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala
        Dick Hirsch made changes -
        Status In Progress [ 3 ] Resolved [ 5 ]
        Resolution Fixed [ 1 ]

          People

          • Assignee:
            Dick Hirsch
            Reporter:
            Ethan Jewett
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development