All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jove.notebook.services.Contents.scala Maven / Gradle / Ivy

The newest version!
package jove
package notebook
package services

import java.io.{PrintWriter, File}
import java.nio.file.Files

import jove.helpers.Kernels
import jove.notebook.Protocol.CheckPoint
import jove.notebook.components.Content
import org.http4s.dsl._
import org.joda.time.DateTime
import org.http4s.argonaut._
import argonaut._, Argonaut._, Shapeless._

import scalaz._, Scalaz._

object Contents extends LazyLogging {
  def apply(content: Content, kernel: Kernels): Plan = {
    object Content extends PrefixedDecodedPath(Root / "api" / "contents");

    def get(path: List[String]) =
      if (path.lastOption == Some("checkpoints"))
        for {
          f <- content.fileFor(path dropRight 1)
          cp = content.checkpointFile(f, content.checkpointId)
          lastModifiedOpt <- \/.fromTryCatchNonFatal {
            if (cp.exists())
              Some(new DateTime(cp.lastModified()))
            else
              None
          } .leftMap(_.toString)
        } yield {
          lastModifiedOpt match {
            case Some(lm) =>
              List(CheckPoint(content.checkpointId, lm.toString))
            case None =>
              Nil
          }
        } .asJson
      else
      // FIXME Set Location & Last-Modified in response
        content.model(path, withContent = true).map(_.asJson)

    def post(path: List[String], reqJsonOpt: Option[Json]) =
      if (path.lastOption == Some("checkpoints"))
        for {
          f <- content.fileFor(path dropRight 1)
          cp = content.checkpointFile(f, content.checkpointId)
          _ <- \/.fromTryCatchNonFatal {
            val par = cp.getParentFile
            if (!par.exists())
              par.mkdirs()
            cp.delete()
            Files.copy(f.toPath, cp.toPath)
          } .leftMap(_.toString)
          lastModified <- \/.fromTryCatchNonFatal {
            new DateTime(cp.lastModified())
          } .leftMap(_.toString)
        } yield
          List(CheckPoint(content.checkpointId, lastModified.toString)).asJson
      else {
        val reqJson = reqJsonOpt getOrElse Json.obj()
        val _type = (reqJson.hcursor --\ "type").as[String].result getOrElse "file"
        val extOpt = (reqJson.hcursor --\ "ext").as[String].result.toOption orElse {
          for {
            () <- Some(()) if _type == "notebook"
            kernelId <- (reqJson.hcursor --\ "kernel_name").as[String].result.toOption.filter(_.nonEmpty) orElse kernel.defaultKernel
            (info, _) <- kernel.kernelsWithInfo.get(kernelId)
            ext <- info.extensions.headOption
          } yield ext
        }

        for {
          d <- content.fileFor(path).flatMap(f => if (f.isDirectory) \/-(f) else -\/("Not a directory"))
          name = content.newIn(d, _type, extOpt, count = 0)
          m <- content.model(new File(d, name), path :+ name, withContent = false)
        } yield m.asJson
      }

    {
      case GET -> Root / "api" / "contents" =>
        get(Nil)

      case req @ (POST -> Root / "api" / "contents") =>
        req.decode[Option[Json]] { reqJsonOpt =>
          post(Nil, reqJsonOpt)
        }

      case GET -> Content(path) =>
        get(path)

      case req @ (PUT -> Content(path)) =>
        // FIXME Handle form content or query string params?
        req.decode[Option[Json]] { reqJsonOpt =>
          for {
            f <- content.fileFor(path)
            _type = reqJsonOpt.flatMap(_.hcursor.--\("type").as[String].result.toOption) getOrElse "file"
            _ <- \/.fromTryCatchNonFatal {
              if (!f.exists()) content.create(_type, f)
            }.leftMap(_.toString)
            contentOpt <- {
              if (_type == "directory")
                \/-(None)
              else
                reqJsonOpt.flatMap(_.hcursor.--\("content").focus.map(_.spaces2)).toRightDisjunction("No content").map(Some(_))
            }
            _ <- \/.fromTryCatchNonFatal {
              for (content <- contentOpt) {
                val w = new PrintWriter(f)
                w write content
                w.close()
              }
            }.leftMap(_.toString)
            m <- content.model(f, path, withContent = false)
          } yield m
        }

      case req @ (PATCH -> Content(path)) =>
        // FIXME Handle form content or query string params?
        req.decode[Option[Json]] { reqJsonOpt =>
          for {
            source <- content.fileFor(path).flatMap(f => if (f.exists()) f.right else "File not found".left)
            destPath <- reqJsonOpt.flatMap(_.hcursor.--\("path").as[String].result.toOption) toRightDisjunction "No path specified"
            dest <- content.fileFor(destPath)
            _ <- \/.fromTryCatchNonFatal {
              Files.move(source.toPath, dest.toPath)
            }.leftMap(_.toString)
            m <- content.model(dest, content.splitPath(destPath), withContent = false)
          } yield m
        }

      case req @ (POST -> Content(path)) =>
        req.decode[Option[Json]] { reqJsonOpt =>
          post(path, reqJsonOpt)
        }

      case DELETE -> Content(path) =>
        for {
          f <- content.fileFor(path)
          _ <- \/.fromTryCatchNonFatal {
            logger info s"Deleting ${f.getAbsolutePath} / ${f.getAbsoluteFile.toPath.toString}"
            Files.delete(f.getAbsoluteFile.toPath)
          } .leftMap(_.toString)
        } yield ()
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy