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