Author: rhirsch
Date: Sun Nov 15 08:23:39 2009
New Revision: 836338

URL: http://svn.apache.org/viewvc?rev=836338&view=rev
Log:
[ESME-14] Redesign, rework, write unit tests for, and fully document API
Patch from Ethan Jewett applied
Changes to Test to get all three current tests to work

Added:
    
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/API2Test.scala
Modified:
    incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala
    incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/ApiTest.scala
    
incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala?rev=836338&r1=836337&r2=836338&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala 
(original)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/API2.scala 
Sun Nov 15 08:23:39 2009
@@ -18,13 +18,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
- 
- /*
+
+/*
  * API2.scala
  *
  * To change this template, choose Tools | Template Manager
  * and open the template in the editor.
- */
+ */            
 
 package org.apache.esme.api
 
@@ -46,7 +46,7 @@
 import scala.collection.mutable.ListBuffer
 import java.util.logging._
 
-object API2 extends ApiHelper {
+object API2 extends ApiHelper with XmlHelper {
   val logger: Logger = Logger.getLogger("org.apache.esme.api")
 
   def dispatch: LiftRules.DispatchPF = {
@@ -57,13 +57,14 @@
     case Req("api2" :: "users" :: Nil, _, GetRequest) => allUsers
 // Add a method to get detail for a specific user
 
-// Document the fact that tag is no longer a parameter here
+// Document the fact that tag is no longer a parameter here                   
+    case Req("api2" :: "user" :: "messages" :: Nil, _, GetRequest)
+               if S.param("timeout").isDefined => waitForMsgs 
     case Req("api2" :: "user" :: "messages" :: Nil, _, GetRequest) => 
allUserMsgs
 // Document the new method for getting messages belonging to a particular tag
     case Req("api2" :: "user" :: "messages" :: "tag" :: tag :: Nil, _, 
GetRequest)
                    => () => allUserMsgs(tag)
 // Possibly deprecate and move to api2/messages or api2/pools/poolName/messages
-// Add back long-poll option
     case Req("api2" :: "user" :: "messages" :: Nil, _, PostRequest) => () => 
addMsg
 
     case Req("api2" :: "user" :: "followees" :: Nil, _, GetRequest) => 
allFollowees         
@@ -105,7 +106,7 @@
   }
 
   def allSessions(): LiftResponse = {
-    val r: Box[NodeSeq] = 
+    val r: Box[Elem] = 
                for (user <- User.currentUser ?~ 
S.?("base_rest_api_err_not_logged_in"))
        yield { 
                        <session>{userToXml(user)}</session>
@@ -115,7 +116,7 @@
   }      
 
   def addSession(): LiftResponse = {
-    val r: Box[NodeSeq] = if (User.loggedIn_?) Empty else
+    val r: Box[Elem] = if (User.loggedIn_?) Empty else
     for (token <- S.param("token") ?~ S.?("base_rest_api_err_missing_param", 
"token");
          auth <- AuthToken.find(By(AuthToken.uniqueId, token))
          ?~ "Token not found";
@@ -132,8 +133,10 @@
   } 
 
   def removeSession(): LiftResponse = {
-    User.logUserOut()
-    true
+    if (true) {
+               User.logUserOut()
+       true
+       } else false
   } 
 
 
@@ -144,24 +147,24 @@
   }
        
   def allUserMsgs(): LiftResponse = {
-    val r: Box[Node] = 
+    val r: Box[Elem] = 
       for (user <- calcUser ?~  S.?("base_rest_api_err_param_not_found", 
"User");
         val lst = Mailbox.mostRecentMessagesFor(user.id, 40))
       yield {
-               <messages>{lst.flatMap{ case (msg, why, _) => msg.toXml % 
why.attr}}</messages>
+               <messages>{lst.flatMap{ case (msg, _, _) => 
msgToXml(msg)}}</messages>
          }
 
     r
   }
 
   def allUserMsgs(tag: String): LiftResponse = {
-       val r: Box[Node] =
+       val r: Box[Elem] =
       for (tagName <- Box(List(tag));
         tag <- Tag.find(By(Tag.name, tagName)))
       yield {
            <tag>
                  <name>{tag.name}</name>
-                 <messages>{tag.findMessages.map(_.toXml)}</messages>
+                 <messages>{tag.findMessages.map(msgToXml(_))}</messages>
            </tag>
          }
        
@@ -359,12 +362,27 @@
          id <- conversationId.map(toLong) ?~ 
S.?("base_rest_api_err_missing_param", "id")
     ) yield <conversation id={id.toString}>{
         Message.findAndPrime(By(Message.conversation, id),
-                            OrderBy(Message.id, Ascending)).map(_.toXml)
+                             OrderBy(Message.id, Ascending)).map(_.toXml)
       }</conversation>
 
     ret
   }
 
+  def waitForMsgs(): LiftResponse = {
+    val future = new LAFuture[List[(Message, MailboxReason)]]()
+    
+    def waitForAnswer: Box[List[(Message, MailboxReason)]] = 
+      future.get(6L * 60L * 1000L)
+
+    var r: Box[NodeSeq] = 
+    for (act <- restActor.is ?~ "No REST actor";
+         val ignore = act ! ListenFor(future, 5 minutes);
+         answer <- waitForAnswer ?~ "Didn't get an answer")
+    yield answer.flatMap{ case (msg, reason) => msgToXml(msg) }
+
+    r
+  }
+
   private def findAction(actionId: Box[String]): Box[Action] =
        for (user <- User.currentUser ?~ S.?("base_rest_api_err_not_logged_in");
          id <- actionId ?~ S.?("base_rest_api_err_missing_param", "id");
@@ -378,12 +396,9 @@
        User.currentUser 
 
   def createTag(in: NodeSeq) = <api_response>{in}</api_response>
-
-  private def userToXml(user: User): Elem = 
-       
<user><id>{user.id.toString}</id><nickname>{user.niceName}</nickname><image>{user.image}</image><whole_name>{user.wholeName}</whole_name></user>
   
   private def buildActor(userId: Long): RestActor = {
-   val ret = new RestActor
+    val ret = new RestActor
     ret ! StartUp(userId)
     ret
   }
@@ -393,31 +408,31 @@
   }
   
 
- class RestActor extends LiftActor {
+  class RestActor extends LiftActor {
     private var userId: Long = _
     private var msgs: List[(Message, MailboxReason)] = Nil
     private var listener: Box[LAFuture[List[(Message, MailboxReason)]]] = Empty
     
-   protected def messageHandler = {
+    protected def messageHandler = {
       case StartUp(userId) =>
-          this.userId = userId
-          Distributor ! Distributor.Listen(userId, this)
+        this.userId = userId
+        Distributor ! Distributor.Listen(userId, this)
 
-        case ByeBye =>
-          Distributor ! Distributor.Unlisten(userId, this)
+      case ByeBye =>
+        Distributor ! Distributor.Unlisten(userId, this)
           
       case UserActor.MessageReceived(msg, reason) =>
         msgs = (msg, reason) :: msgs
-      listener.foreach {
-        who =>
-          who.satisfy(msgs)
-        listener = Empty
-        msgs = Nil
-      }
+        listener.foreach {
+          who =>
+            who.satisfy(msgs)
+            listener = Empty
+            msgs = Nil
+        }
       
       case ReleaseListener =>
         listener.foreach(_.satisfy(Nil))
-      listener = Empty
+        listener = Empty
       
       case ListenFor(who, len) =>
         msgs match {
@@ -428,7 +443,7 @@
              
             case xs =>
               who.satisfy(xs)
-             msgs = Nil
+              msgs = Nil
               listener = Empty
         }
     }
@@ -443,10 +458,7 @@
 }                                                          
 
 // TODO:
-// 1. Get rid of calcUser and replace with User.currentUser ?~ 
S.?("base_rest_api_err_not_logged_in")
 // 2. Fix errors so that they properly indicate a missing parameter or 404
 // 3. Change changeAction so that if the "enabled" parameter doesn't show up 
it will simply use
 //    the current value for the action, not throw an error.
-// 4. Match based on the return content type header to determine what to 
return (default to XML)    
-// 5. Re-enable streaming message API (using comet approach)
-
+// 4. Match based on the return content type header to determine what to 
return (default to XML) 

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala?rev=836338&r1=836337&r2=836338&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala 
(original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala 
Sun Nov 15 08:23:39 2009
@@ -218,9 +218,25 @@
     def sourceAttr: Option[NodeSeq] = is match {
       case null | "" => None
       case xs => Some(Text(xs))
-    }
+     }
+   }
+ 
+  lazy val body: String = {
+       val org = originalXml
+
+    (org \ "body").map(_.child map {
+      case e: Elem => e.text
+      case x => x.text
+    }).first.first
+  }
+
+  lazy val metaData: String = {
+    val org = originalXml
+
+    (org \ "metadata").map(_.text).first
   }
 
+
   object replyTo extends MappedLongForeignKey(this, Message)
 
   object conversation extends MappedLongForeignKey(this, Message)

Added: 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/API2Test.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/API2Test.scala?rev=836338&view=auto
==============================================================================
--- 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/API2Test.scala 
(added)
+++ 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/API2Test.scala 
Sun Nov 15 08:23:39 2009
@@ -0,0 +1,78 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ * 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.esme.api
+
+import org.specs._
+import org.specs.runner.JUnit3
+import org.specs.runner.ConsoleRunner
+import net.liftweb.util._
+import net.liftweb.common._
+import org.specs.matcher._
+import Helpers._
+import net.sourceforge.jwebunit.junit.WebTester
+import org.mortbay.jetty.Server
+import org.mortbay.jetty.servlet.{Context, FilterHolder}
+import org.mortbay.jetty.servlet.ServletHolder
+import org.mortbay.jetty.webapp.WebAppContext
+import org.apache.esme._
+import model._
+import net.liftweb.http._
+import testing.{ReportFailure, TestKit, HttpResponse, TestFramework}
+
+import net.sourceforge.jwebunit.junit.WebTester
+import _root_.junit.framework.AssertionFailedError                 
+
+class Api2SpecsAsTest extends JUnit3(Api2Specs)
+object Api2SpecsRunner extends ConsoleRunner(Api2Specs)
+
+object Api2Specs extends Specification with TestKit {
+  JettyTestServer.start
+
+  val baseUrl = JettyTestServer.urlFor("")
+
+  implicit val reportError = new ReportFailure {
+    def fail(msg: String): Nothing = Api2Specs.this.fail(msg)
+  }
+  
+    def shouldnt(f: => Unit): Unit =
+    try {
+      val x = f
+      fail("Shouldn't succeed")
+    } catch {
+      case _ => ()
+    }
+                         
+  "allUsers" should {
+    "have a response code of 200" in {
+      API2.allUsers().toResponse.code must be equalTo(200)
+    }
+
+       "return a node listing all users" in {                    
+         API2.allUsers().toResponse.toString must include(
+"""<api_response><users><user><id>1</id><nickname>hash</nickname><image>None</image><whole_name>hash</whole_name></user></users></api_response>"""
+         )                                         
+       }
+  }
+
+                                    
+}
+ 
\ No newline at end of file

Modified: 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/ApiTest.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/ApiTest.scala?rev=836338&r1=836337&r2=836338&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/ApiTest.scala 
(original)
+++ 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/api/ApiTest.scala 
Sun Nov 15 08:23:39 2009
@@ -45,7 +45,7 @@
 object ApiSpecsRunner extends ConsoleRunner(ApiSpecs)
 
 object ApiSpecs extends Specification with TestKit {
-  JettyTestServer.start
+ JettyTestServer.start 
 
   val baseUrl = JettyTestServer.urlFor("")
 
@@ -88,4 +88,6 @@
 
 
   }
+  
+
 }

Modified: 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala?rev=836338&r1=836337&r2=836338&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala
 (original)
+++ 
incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala
 Sun Nov 15 08:23:39 2009
@@ -37,6 +37,7 @@
 import org.apache.esme._
 import model._
 import net.liftweb.http._
+import testing.{ReportFailure, TestKit, HttpResponse, TestFramework}
 
 import net.sourceforge.jwebunit.junit.WebTester
 import _root_.junit.framework.AssertionFailedError
@@ -44,9 +45,23 @@
 class MsgParserSpecsAsTest extends JUnit3(MsgParserSpecs)
 object MsgParserSpecsRunner extends ConsoleRunner(MsgParserSpecs)
 
-object MsgParserSpecs extends Specification {
-  JettyTestServer.start
+object MsgParserSpecs extends Specification with TestKit { 
+  JettyTestServer.start 
 
+  val baseUrl = JettyTestServer.urlFor("")
+
+  implicit val reportError = new ReportFailure {
+    def fail(msg: String): Nothing = MsgParserSpecs.this.fail(msg)
+  }
+  
+    def shouldnt(f: => Unit): Unit =
+    try {
+      val x = f
+      fail("Shouldn't succeed")
+    } catch {
+      case _ => ()
+    }
+   
   type PFT = MsgParser.ParseResult[_]
   def parseMatch(name: String, matchr: PartialFunction[PFT, Any]) = new 
Matcher[PFT] {
     def apply(v: => PFT) = (matchr.isDefinedAt(v),
@@ -291,7 +306,8 @@
         })
 
     }
-
+  
   }
+  
 
 }


Reply via email to