~ work around the content-types for GET and PUT
authorAlexandre Bertails <bertails@gmail.com>
Sat, 15 Oct 2011 10:12:34 -0400
changeset 65 68e9d1a12e27
parent 61 938b40aaea88
child 66 42222285bfea
child 78 abbf9e663c35
~ work around the content-types for GET and PUT
src/main/scala/AcceptLang.scala
src/main/scala/Lang.scala
src/main/scala/Post.scala
src/main/scala/plan.scala
src/main/scala/rdfLanguage.scala
src/test/scala/CreateContentSpecs.scala
src/test/scala/util/specs.scala
src/test/scala/util/utiltest.scala
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/AcceptLang.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -0,0 +1,13 @@
+package org.w3.readwriteweb
+
+import unfiltered.request._
+
+object AcceptLang {
+  
+  def unapply(req: HttpRequest[_]): Option[Lang] =
+    Accept(req) map Lang.apply collectFirst { case Some(lang) => lang }
+  
+  def apply(req: HttpRequest[_]): Option[Lang] =
+    unapply(req)
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/Lang.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -0,0 +1,49 @@
+package org.w3.readwriteweb
+
+import unfiltered.request._
+
+sealed trait Lang {
+  
+  def contentType = this match {
+    case RDFXML => "application/rdf+xml"
+    case TURTLE => "text/turtle"
+    case N3 => "text/n3"
+  }
+  
+  def jenaLang = this match {
+    case RDFXML => "RDF/XML-ABBREV"
+    case TURTLE => "TURTLE"
+    case N3 => "N3"
+  }
+  
+}
+
+object Lang {
+  
+  val supportedLanguages = Seq(RDFXML, TURTLE, N3)
+  val supportContentTypes = supportedLanguages map (_.contentType)
+  val supportedAsString = supportContentTypes mkString ", "
+  
+  val default = RDFXML
+  
+  def apply(contentType: String): Option[Lang] =
+    contentType match {
+      case "text/n3" => Some(N3)
+      case "text/turtle" => Some(TURTLE)
+      case "application/rdf+xml" => Some(RDFXML)
+      case _ => None
+  }
+
+  def apply(req: HttpRequest[_]): Option[Lang] =
+    RequestContentType(req) flatMap Lang.apply
+    
+  def unapply(req: HttpRequest[_]): Option[Lang] =
+    apply(req)
+
+}
+
+case object RDFXML extends Lang
+
+case object TURTLE extends Lang
+
+case object N3 extends Lang
--- a/src/main/scala/Post.scala	Thu Oct 13 19:59:40 2011 -0400
+++ b/src/main/scala/Post.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -11,21 +11,21 @@
 import com.hp.hpl.jena.shared.JenaException
 
 sealed trait Post
-
 case class PostUpdate(update: UpdateRequest) extends Post
-
 case class PostRDF(model: Model) extends Post
-
 case class PostQuery(query: Query) extends Post
-
 case object PostUnknown extends Post
 
-
 import scalaz._
 import Scalaz._
 
 object Post {
   
+  val SparqlContentType = "application/sparql-query"
+  val supportContentTypes = SparqlContentType + Lang.supportContentTypes
+  val supportedAsString = supportContentTypes mkString ", "
+
+  
   val logger: Logger = LoggerFactory.getLogger(this.getClass)
 
   def parse(is: InputStream, baseURI:String): Post = {
--- a/src/main/scala/plan.scala	Thu Oct 13 19:59:40 2011 -0400
+++ b/src/main/scala/plan.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -65,19 +65,28 @@
         case GET(_) | HEAD(_) =>
           for {
             model <- r.get() failMap { x => NotFound }
-            lang = Lang.fromRequest(req)
+            lang = AcceptLang(req) getOrElse Lang.default
           } yield {
             req match {
               case GET(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType) ~> ResponseModel(model, baseURI, lang)
               case HEAD(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType)
             }
           }
-        case PUT(_) =>
+        case PUT(_) & Lang(lang) =>
           for {
             bodyModel <- modelFromInputStream(Body.stream(req), baseURI) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
             _ <- r.save(bodyModel) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString) }
           } yield Created
-        case POST(_) => {
+        case PUT(_) =>
+          BadRequest ~> ResponseString("Content-Type MUST be one of: " + Lang.supportedAsString)
+        case POST(_) =>
+          req match {
+            case RequestContentType("application/sparql-query") => null
+            case RequestContentType(ct) if Lang.supportContentTypes contains ct => null
+            case _ => BadRequest ~> ResponseString("Content-Type MUST be one of: " + Post.supportedAsString)
+          }
+          
+          {
           Post.parse(Body.stream(req), baseURI) match {
             case PostUnknown => {
               logger.info("Couldn't parse the request")
@@ -103,7 +112,7 @@
             }
             case PostQuery(query) => {
               logger.info("SPARQL Query:\n" + query.toString())
-              lazy val lang = Lang.fromRequest(req)
+              lazy val lang = Lang(req) getOrElse Lang.default
               for {
                 model <- r.get() failMap { t => NotFound }
               } yield {
--- a/src/main/scala/rdfLanguage.scala	Thu Oct 13 19:59:40 2011 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package org.w3.readwriteweb
-
-import unfiltered.request._
-
-sealed trait Lang {
-  
-  def contentType = this match {
-    case RDFXML => "application/rdf+xml"
-    case TURTLE => "text/turtle"
-    case N3 => "text/n3"
-  }
-  
-  def jenaLang = this match {
-    case RDFXML => "RDF/XML-ABBREV"
-    case TURTLE => "TURTLE"
-    case N3 => "N3"
-  }
-  
-}
-
-object Lang {
-  
-  val default = RDFXML
-  
-  def apply: PartialFunction[String, Lang] = {
-    case "text/n3" => N3
-    case "text/turtle" => TURTLE
-    case "application/rdf+xml" => RDFXML
-  }
-
-  def fromRequest(req: HttpRequest[_]): Lang = {
-    val contentType = Accept(req).headOption
-    contentType map { Lang.apply } getOrElse RDFXML
-  }
-
-}
-
-case object RDFXML extends Lang
-
-case object TURTLE extends Lang
-
-case object N3 extends Lang
--- a/src/test/scala/CreateContentSpecs.scala	Thu Oct 13 19:59:40 2011 -0400
+++ b/src/test/scala/CreateContentSpecs.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -9,7 +9,7 @@
 
   "PUTing an RDF document on Joe's URI (which does not exist yet)" should {
     "return a 201" in {
-      val httpCode = Http(uri.put(rdfxml) get_statusCode)
+      val httpCode = Http(uri.put(RDFXML, rdfxml) get_statusCode)
       httpCode must_== 201
     }
     "create a document on disk" in {
@@ -76,7 +76,7 @@
 
   """PUTting not-valid RDF to Joe's URI""" should {
     "return a 400 Bad Request" in {
-      val statusCode = Http.when(_ == 400)(uri.put("that's bouleshit") get_statusCode)
+      val statusCode = Http.when(_ == 400)(uri.put(RDFXML, "that's bouleshit") get_statusCode)
       statusCode must_== 400
     }
   }
--- a/src/test/scala/util/specs.scala	Thu Oct 13 19:59:40 2011 -0400
+++ b/src/test/scala/util/specs.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -81,7 +81,7 @@
 trait SomeDataInStore extends FilesystemBased with SomeRDF with SomeURI {
   
   doBeforeSpec {
-    val httpCode = Http(uri.put(rdfxml) get_statusCode)
+    val httpCode = Http(uri.put(RDFXML, rdfxml) get_statusCode)
     httpCode must_== 201
   }
   
--- a/src/test/scala/util/utiltest.scala	Thu Oct 13 19:59:40 2011 -0400
+++ b/src/test/scala/util/utiltest.scala	Sat Oct 15 10:12:34 2011 -0400
@@ -27,40 +27,41 @@
 
 package object utiltest {
   
-  def baseURI(req:Request):String = "%s%s" format (req.host, req.path)
+  def baseURI(req: Request): String = "%s%s" format (req.host, req.path)
   
-  def beIsomorphicWith(that:Model):Matcher[Model] =
+  def beIsomorphicWith(that: Model): Matcher[Model] =
     new Matcher[Model] {
-      def apply(otherModel: => Model) =
+      def apply(otherModel:  => Model) =
         (that isIsomorphicWith otherModel,
          "Model A is isomorphic to model B",
          "%s not isomorphic with %s" format (otherModel.toString, that.toString))
   }
   
-  class RequestW(req:Request) {
+  class RequestW(req: Request) {
 
-    def as_model(base:String, lang:String = "RDF/XML-ABBREV"):Handler[Model] =
+    def as_model(base: String, lang: String = "RDF/XML-ABBREV"): Handler[Model] =
       req >> { is => modelFromInputStream(is, base, lang).toOption.get }
 
-    def post(body:String):Request =
+    def post(body: String): Request =
       (req <<< body).copy(method="POST")
       
-    def put(body:String):Request = req <<< body
+    def put(lang: Lang, body: String): Request =
+      req <:< Map("Content-Type" -> lang.contentType) <<< body
       
-    def get_statusCode:Handler[Int] = new Handler(req, (c, r, e) => c, { case t => () })
+    def get_statusCode: Handler[Int] = new Handler(req, (c, r, e) => c, { case t => () })
     
-    def get_header(header:String):Handler[String] = req >:> { _(header).head }
+    def get_header(header: String): Handler[String] = req >:> { _(header).head }
     
-    def get:Request = req.copy(method="GET")
+    def get: Request = req.copy(method="GET")
     
-    def >++ [A, B, C] (block: Request => (Handler[A], Handler[B], Handler[C])) = {
+    def >++ [A, B, C] (block:  Request => (Handler[A], Handler[B], Handler[C])) = {
       Handler(req, { (code, res, opt_ent) =>
         val (a, b, c) = block( /\ )
           (a.block(code, res, opt_ent), b.block(code,res,opt_ent), c.block(code,res,opt_ent))
       } )
     }
     
-    def >+ [A, B] (block: Request => (Handler[A], Handler[B])) = {
+    def >+ [A, B] (block:  Request => (Handler[A], Handler[B])) = {
       Handler(req, { (code, res, opt_ent) =>
         val (a, b) = block( /\ )
         (a.block(code, res, opt_ent), b.block(code,res,opt_ent))
@@ -69,7 +70,7 @@
     
   }
   
-  implicit def wrapRequest(req:Request):RequestW = new RequestW(req)
+  implicit def wrapRequest(req: Request): RequestW = new RequestW(req)