POST of rdf to directory creates resource + returns 201 webid
authorHenry Story <henry.story@bblfish.net>
Tue, 10 Apr 2012 15:50:51 +0200
branchwebid
changeset 191 5cf67ba5c0d4
parent 190 c3cb0e73cf86
child 192 bf32780aa336
POST of rdf to directory creates resource + returns 201
project/build.scala
src/main/scala/Filesystem.scala
src/main/scala/GraphCache.scala
src/main/scala/Lang.scala
src/main/scala/ReadWriteWeb.scala
src/main/scala/Resource.scala
src/test/scala/CreateContentSpecs.scala
src/test/scala/util/specs.scala
src/test/scala/util/utiltest.scala
--- a/project/build.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/project/build.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -7,7 +7,7 @@
 //  val specs = "org.scala-tools.testing" %% "specs" % "1.6.9" % "test"
   val specs2 = "org.specs2" %% "specs2" % "1.6.1"
   val specs2_scalaz =  "org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test"
-  val dispatch_version = "0.8.6"
+  val dispatch_version = "0.8.8"
   val dispatch_http = "net.databinder" %% "dispatch-http" % dispatch_version 
   val dispatch_nio = "net.databinder" %% "dispatch-nio" % dispatch_version 
   val unfiltered_version = "0.6.1"
--- a/src/main/scala/Filesystem.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/main/scala/Filesystem.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -10,7 +10,7 @@
 import Scalaz._
 
 import scala.sys
-import java.nio.file.Files
+import java.nio.file.{StandardOpenOption, Files}
 
 class Filesystem(
   baseDirectory: File,
@@ -101,6 +101,21 @@
     } catch {
       case e: IOException => e.fail
     }
+
+    def create(): Validation[Throwable, Resource] =  {
+       if (!fileOnDisk.exists())
+         new Throwable("Must first create "+name()).fail
+       else if (!fileOnDisk.isDirectory)
+         new Throwable("Can only create a resource in a directory/collection which this is not "+name()).fail
+       else try {
+         val path = Files.createTempFile(fileOnDisk.toPath,"res",lang.suffix)
+         resource(new URL(name(),path.getFileName.toString)).success
+       } catch {
+         case ioe: IOException => ioe.fail
+       }
+    }
+
+
   }
   
 }
--- a/src/main/scala/GraphCache.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/main/scala/GraphCache.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -116,6 +116,9 @@
     def createDirectory(model: Model) =  throw new MethodNotSupportedException("not implemented")
 
     def delete = throw new MethodNotSupportedException("not implemented")
+
+    def create() = throw new MethodNotSupportedException("not implemented")
+
   }
 
   private def getUrl(u: URL) = {
--- a/src/main/scala/Lang.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/main/scala/Lang.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -6,6 +6,14 @@
 package org.w3.readwriteweb
 
 sealed trait Lang {
+
+  def suffix = this match {
+    case RDFXML => ".rdf"
+    case TURTLE => ".ttl"
+    case N3 => ".n3"
+    case XHTML => ".xhtml"
+    case HTML => ".html"
+  }
   
   def contentType = this match {
     case RDFXML => "application/rdf+xml"
--- a/src/main/scala/ReadWriteWeb.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/main/scala/ReadWriteWeb.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -15,7 +15,8 @@
               QueryTypeConstruct => CONSTRUCT,
               QueryTypeDescribe => DESCRIBE}
 
-import scalaz.{Resource => _}
+import scalaz.{Scalaz, Resource => _}
+import Scalaz._
 import unfiltered.request._
 import unfiltered.Cycle
 import unfiltered.response._
@@ -111,6 +112,23 @@
               } yield Created
             case PUT(_) =>
               BadRequest ~> ResponseString("Content-Type MUST be one of: " + Lang.supportedAsString)
+            case POST(_) & RequestContentType(ct) if representation == DirectoryRepr =>
+              r.create() failMap { t => NotFound ~> ResponseString(t.getStackTraceString)} flatMap { rNew =>
+                Post.parse(Body.stream(req), rNew.name, ct) match {
+                  case PostRDF(model) => {
+                    logger.info("RDF content:\n" + model.toString())
+                    for {
+                      model <- rNew.save(model) failMap {
+                        t => InternalServerError ~> ResponseString(t.getStackTraceString)
+                      }
+                    } yield Created ~> ResponseHeader("Location",Seq(rNew.name.toString))
+                  }
+                  case _ => {
+                    logger.info("Couldn't parse the request")
+                    (BadRequest ~> ResponseString("You MUST provide valid content for given Content-Type: " + ct)).success
+                  }
+                }
+              }
             case POST(_) & RequestContentType(ct) if Post.supportContentTypes contains ct => {
               Post.parse(Body.stream(req), uri, ct) match {
                 case PostUnknown => {
--- a/src/main/scala/Resource.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/main/scala/Resource.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -22,6 +22,9 @@
   def get(policy: CacheControl.Value = CacheControl.CacheFirst): Validation[Throwable, Model]
   def delete: Validation[Throwable, Unit]
   def save(model:Model):Validation[Throwable, Unit]
+
+  //These two methods only work when called on directories
   def createDirectory(model: Model): Validation[Throwable, Unit]
+  def create(): Validation[Throwable, Resource]
 }
 
--- a/src/test/scala/CreateContentSpecs.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/test/scala/CreateContentSpecs.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -4,6 +4,8 @@
 import org.w3.readwriteweb.utiltest._
 
 import dispatch._
+import java.net.URL
+import java.io.File
 
 object PutRDFXMLSpec extends SomePeopleDirectory {
 
@@ -68,6 +70,28 @@
       model must beIsomorphicWith (expectedFinalModel)
     }
   }
+
+  "POSTing an RDF document to a Joe's directory/collection" should {
+    "succeed and create a resource on disk" in {
+      val handler = dirUri.post(diffRDF, RDFXML) >+ { req =>
+          val loc: Handler[String] = req.get_header("Location")
+          val status_code: Handler[Int] = req.get_statusCode
+          (status_code,loc)
+      }
+      val (code, head) = Http(handler)
+      System.out.println("code="+code)
+      System.out.println("head="+head)
+      code must_== 201
+      val headURI = new URL(head.trim)
+      System.out.println("root="+root)
+      val file = new File(root, headURI.getPath.substring(baseURL.size))
+      file must exist
+    }
+
+    "create a resource on disk" in {
+//      joeProfileOnDisk must be file
+    }
+  }
   
 }
 
--- a/src/test/scala/util/specs.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/test/scala/util/specs.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -89,8 +89,10 @@
 trait SomeURI extends FilesystemBased {
   
   val emptyModel = com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel()
-  
-  lazy val dirUri = host / "wiki/people/"
+
+  val peopleDirPath = "wiki/people/"
+
+  lazy val dirUri = host / peopleDirPath
   
   lazy val uri = host / "wiki/people/JoeLambda"
   
--- a/src/test/scala/util/utiltest.scala	Mon Apr 09 18:03:56 2012 +0200
+++ b/src/test/scala/util/utiltest.scala	Tue Apr 10 15:50:51 2012 +0200
@@ -59,7 +59,7 @@
     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: Request = req.copy(method="GET")
     
     def >++ [A, B, C] (block:  Request => (Handler[A], Handler[B], Handler[C])) = {