Permalink
Browse files

version upgrades for dependencies, refactoring and added test cases

  • Loading branch information...
1 parent 372e7af commit 5d85e01f523e276988514adcb3b5cb1ef66de88e Debasish Ghosh committed Jul 1, 2011
@@ -9,8 +9,8 @@ class SCouchDbProject(info: ProjectInfo) extends DefaultProject(info)
val dispatch_http = "net.databinder" % "dispatch-http_2.9.0-1" % "0.8.3" % "compile"
val commons_logging = "commons-logging" % "commons-logging" % "1.1.1" % "compile"
val httpclient = "org.apache.httpcomponents" % "httpclient" % "4.1.1" % "compile"
- val sjson = "net.debasishg" % "sjson_2.9.0" % "0.11" % "compile"
+ val sjson = "net.debasishg" % "sjson_2.9.0-1" % "0.13" % "compile"
- val scalatest = "org.scalatest" % "scalatest_2.9.0" % "1.4.1" % "test"
+ val scalatest = "org.scalatest" % "scalatest_2.9.0" % "1.6.1" % "test"
val junit = "junit" % "junit" % "4.8.1"
}
@@ -1,12 +1,11 @@
package scouch.db
import java.net.URLEncoder.encode
-import sjson.json.Implicits._
+// import sjson.json.Implicits._
import dispatch._
import dispatch.json._
import dispatch.json.Js._
import JsHttp._
-// import RichRequest._
import DbUtils._
import sjson.json._
@@ -24,21 +24,25 @@ object DesignDocument {
case class DesignDocument(var _id: String,
@(JSONProperty @getter)(ignoreIfNull = true, ignore = false) _rev: String,
@(JSONTypeHint @field)(value = classOf[View]) views: Map[String, View],
- @(JSONProperty @getter)(ignoreIfNull = true, ignore = false) validate_doc_update: String) {
+ @(JSONProperty @getter)(ignoreIfNull = true, ignore = false) validate_doc_update: Option[String] = None,
+ @(JSONProperty @getter)(ignoreIfNull = true, ignore = false) shows: Option[Map[String, String]] = None,
+ @(JSONProperty @getter)(ignoreIfNull = true, ignore = false) filters: Option[Map[String, String]] = None,
+ @(JSONProperty @getter)(ignoreIfNull = true, ignore = false) updates: Option[Map[String, String]] = None,
+ @(JSONProperty @getter)(ignoreIfNull = true, ignore = false) lists: Option[Map[String, String]] = None) {
if (_id != null)
if (!_id.startsWith(DesignDocument.PREFIX))
_id = DesignDocument.extendId(_id)
var language = "javascript"
- private [db] def this() = this(null, null, Map[String, View](), null)
+ private [db] def this() = this(null, null, Map[String, View](), None)
override def toString = {
"_id = " + _id + " _rev = " + _rev + " language = " + language + " " +
(validate_doc_update match {
- case null => ""
- case x => " validate = " + x + " "
+ case Some(x) => " validate = " + x + " "
+ case _ => ""
}) +
(views match {
case null => ""
@@ -102,7 +102,6 @@ class ViewServer(val ps: PrintWriter) {
Nil
}
try {
- import sjson.json.Implicits._
JsBean.toJSON(res.reverse)
} catch {
case e: Exception =>
@@ -133,31 +132,34 @@ class ViewServer(val ps: PrintWriter) {
def add_ddocs(ddocId: String, ddoc: JsValue) = ddocs += ((ddocId, ddoc))
import ViewServerUtils._
- def validate(ddocname: String, funPath: String, doc: JsValue, args: JsValue): Either[String, JsValue] = {
-
+ def process(ddocname: String, funPath: List[String], doc: JsValue, args: JsValue): Either[String, JsValue] = {
try {
val ddoc = ddocs.get(ddocname).getOrElse(sys.error("query protocol error: uncached design doc: " + ddocname))
- val valid = 'validate_doc_update ? str
- val valid(valid_) = ddoc
- val fn = eval(valid_).asInstanceOf[Function3[JsValue, JsValue, Any, Any]]
- val f = fn(doc, doc, args)
+ val fn =
+ funPath match {
+ case cmd :: Nil => // for validate_doc_update we have only a singleton list
+ val c = Symbol(cmd) ? str
+ val c(c_) = ddoc
+ c_
+ case cmd :: f :: Nil => // 2 element list for functions like "shows", "lists" etc.
+ val c = Symbol(cmd) ? obj
+ val f1 = Symbol(f) ? str
+ val c(f1(f_)) = ddoc
+ f_
+ case _ => sys.error("Unhandled function path: [" + funPath + "]")
+ }
+
+ val function = eval(fn).asInstanceOf[Function3[JsValue, JsValue, Any, Any]]
+ val f = function(doc, doc, args)
Left(JsValue.toJson(JsNumber(1)))
} catch {
case se: ScriptException =>
- se.printStackTrace(ps)
- ps.flush
Right(JsValue(Map("error" -> "validation_compilation_error", "reason" -> se.getMessage)))
case vx: ValidationException =>
- vx.printStackTrace(ps)
- ps.flush
Right(JsValue(Map("forbidden" -> vx.getMessage)))
case ux: AuthorizationException =>
- ux.printStackTrace(ps)
- ps.flush
Right(JsValue(Map("unauthorized" -> ux.getMessage)))
case x: Exception =>
- x.printStackTrace(ps)
- ps.flush
Right(JsValue(Map("dummy" -> x.getMessage)))
}
}
@@ -262,21 +264,15 @@ object VS {
/**
* The protocol is
+ *
+ * Step #1
+ * -------
* CouchDB sends:
*
- * ["validate", function string, new document, old document, request]
+ * ["ddoc", "new", design doc name, design document]
*
- * View Server returns:
+ * View Server stores the design doc in a hash hashed by the name and returns "true"
*
- * 1 if successful, otherwise exception having "error" -> "forbidden", "reason" -> anything
- *
- * The key "forbidden" is important - otherwise CouchDB will not send back 403.
- *
- * References:
- * $COUCH_SOURCE/share/server/validate.js
- * $COUCH_SOURCE/share/server/loop.js
- * $COUCH_SOURCE/share/server/util.js
- * $COUCH_HOME/test/query_server_spec.rb
*/
case JsArray(List(JsString("ddoc"), JsString("new"), JsString(ddocname), doc)) => {
v.add_ddocs(ddocname, doc)
@@ -285,19 +281,57 @@ object VS {
p.flush
}
- case JsArray(List(JsString("ddoc"), JsString(ddocname), JsArray(JsString(fun) :: _), JsArray(doc :: _ :: args :: _))) => {
- v.validate(ddocname, fun, doc, args) match {
- case Left(s) => {
- p.write(s)
- p.write('\n')
- p.flush
+ // handle validate
+ // this is the general protocol for handling functions like "shows", "lists" etc.
+ // but the handlers for each of them need to be written
+ // currently "process" only works for validate_doc_update
+
+ /**
+ * The protocol is
+ *
+ * Step #2
+ * -------
+ * CouchDB sends:
+ *
+ * ["ddoc", design doc name, [fun path] (e.g. validate_doc_update), [doc, _, args, _]]
+ *
+ * References:
+ * $COUCH_SOURCE/share/server/validate.js
+ * $COUCH_SOURCE/share/server/loop.js
+ * $COUCH_SOURCE/share/server/util.js
+ * $COUCH_HOME/test/query_server_spec.rb : run it using the trace flags to see the line protocol
+ */
+ case x@JsArray(List(JsString("ddoc"), JsString(ddocname), funpath, JsArray(doc :: _ :: args :: _))) => {
+ val fns: Either[List[String], JsValue] =
+ funpath match {
+ case JsArray(JsString(cmd) :: JsString(fn) :: Nil) => Left(List(cmd, fn))
+ case JsArray(JsString(cmd) :: Nil) => Left(List(cmd))
+ case _ => Right(JsString("Unhandled function list: " + funpath))
}
- case Right(x) => {
+
+ fns.fold(
+ (lst) => {
+ v.process(ddocname, lst, doc, args).fold(
+ (s) => {
+ v.ps.println("from process: " + s)
+ v.ps.flush
+ p.write(s)
+ p.write('\n')
+ p.flush
+ },
+ (x) => {
+ p.write(JsValue.toJson(x))
+ p.write('\n')
+ p.flush
+ }
+ )
+ },
+ (ex) => {
p.write(JsValue.toJson(x))
p.write('\n')
p.flush
}
- }
+ )
}
case _ =>
@@ -308,6 +342,10 @@ object VS {
v.ps.close
}
s = isr.readLine
+ v.ps.println("**")
+ v.ps.println(s)
+ v.ps.println("**")
+ v.ps.flush
}
}
}
@@ -29,6 +29,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
override def afterAll {
http(test.delete)
(http x test) { (status, _, _) => status } should equal (404)
+ Http.shutdown
println("** destroyed database")
}
@@ -46,7 +47,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
}
describe("Create a design document, query by id and update") {
- val d = DesignDocument("foo", null, Map[String, View](), null)
+ val d = DesignDocument("foo", null, Map[String, View]())
val mapfn = "function(doc) { emit(doc.value,doc); }"
val vi = new View(mapfn, null)
var revision: String = null
@@ -75,7 +76,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
revision = sh._2
}
it("update the document with a view") {
- val doc = DesignDocument(d._id, revision, Map("map" -> vi), null)
+ val doc = DesignDocument(d._id, revision, Map("map" -> vi))
http(de update(doc, revision))
nir = http(de ># %(Id._id, Id._rev))
nir._1 should equal(d._id)
@@ -96,7 +97,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
sh._3._rev should equal(sh._2)
}
it("update with incorrect revision should give 409 (conflict in update)") {
- val doc = DesignDocument(d._id, revision, Map("map" -> vi), null) // using same revision as before
+ val doc = DesignDocument(d._id, revision, Map("map" -> vi)) // using same revision as before
intercept[dispatch.StatusCode] {
http(de update(doc, revision))
}
@@ -109,7 +110,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
}
}
it("update with null revision and same matching id should give 409 (conflict in update)") {
- val doc = DesignDocument(d._id, null, Map("map" -> vi), null) // using null revision
+ val doc = DesignDocument(d._id, null, Map("map" -> vi)) // using null revision
try {
http(de update(doc, revision))
}
@@ -287,7 +288,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
}
describe("Create another design document, query by id and update") {
- val d = DesignDocument("lunch", null, Map[String, View](), null)
+ val d = DesignDocument("lunch", null, Map[String, View]())
val mapfn = "function(doc) {\n var store, price, key;\n if (doc.item && doc.prices) {\n for (store in doc.prices) {\n price = doc.prices[store];\n key = [doc.item, price];\n emit(key, store);\n }\n }\n}\n"
val vi = new View(mapfn, null)
var revision: String = null
@@ -316,7 +317,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
revision = sh._2
}
it("update the document with a view") {
- val doc = DesignDocument(d._id, revision, Map("least_cost_lunch" -> vi), null)
+ val doc = DesignDocument(d._id, revision, Map("least_cost_lunch" -> vi))
http(de update(doc, revision))
nir = http(de ># %(Id._id, Id._rev))
nir._1 should equal(d._id)
@@ -652,7 +653,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
describe("Create a design document with pass thru validation function") {
val all_pass = "function(newDoc, oldDoc, userCtx) {}" // all valid
- val d = DesignDocument("foo_valid", null, Map[String, View](), all_pass)
+ val d = DesignDocument("foo_valid", null, Map[String, View](), Some(all_pass))
val de = Doc(test, d._id)
it("creation should be successful") {
@@ -672,7 +673,7 @@ class SCouchDbSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
describe("Create a design document with all-fail validation function") {
val all_fail = "function(newDoc, oldDoc, userCtx) {throw({forbidden : 'no way'});}"
- val d = DesignDocument("foo_invalid", null, Map[String, View](), all_fail)
+ val d = DesignDocument("foo_invalid", null, Map[String, View](), Some(all_fail))
val de = Doc(test, d._id)
it("creation should be successful") {
Oops, something went wrong.

0 comments on commit 5d85e01

Please sign in to comment.