Restapi-rest-mongo application instance: one of the distributed picture management systems, restservice

I agree with a colleague who recently mentioned that he would like to have pictures on the web in MongoDB.MongoDB has many advantages over storing pictures as files in a subdirectory on your hard disk.First, MongoDB is a distributed database where pictures can be stored across servers.In a clustered environment, the technology of replica set, slicing, etc. can improve the speed of picture reading, realize the high availability and security of data.Then, a large number of pictures can be processed in a standard record management mode, and even in a large traffic environment, the cluster node load balancing method can be used to help the picture access.

I thought about ways to make this picture management system use the capabilities of distributed clustering software.MongoDB is a distributed database that can be accessed by any node in a cluster, that is, a unified rest-mongo is deployed on all nodes in the cluster, so that clients can access different nodes with different ip addresses to submit picture access requests.Assuming that one node receives more clients than others, we can put the process of picture access to other nodes that are relatively idle, that is, load balancing.It appears that this system requires components such as MongoDB,rest-mongo and akka-cluster.

Let's start with front-end requirements: there are n pictures for each item on the page, and the client provides the item number, description, default size and picture when requesting to deposit in the system.Make n save and write requests for a commodity, the same commodity number, the system automatically generates a serial number for each picture and returns it to the client in httprespose.When the client fetches the picture, it provides the commodity number. The system first returns all the picture serial numbers of this commodity to the client, then the client requests one picture by sequence number, and specifies the scaling size of the output picture.

In this post, we followed the previous articles to implement rest services for image access.Based on the rest-mongo above, it should be OK to make some targeted modifications to the new system requirements.

First there is the Model section, as follows:

case class WebPic(
                     pid: String,
                     seqno: Int,
                     desc: Option[String],
                     width: Option[Int],
                     heigth: Option[Int],
                     pic: Option[MGOBlob]
                   ) extends ModelBase[Document] { self =>
    override def to: Document = {
      var doc = Document(
        "pid" -> self.pid,
        "seqno" -> self.seqno
      )
      if (self.desc != None)
        doc = doc + ("desc" -> self.desc.get)
      if (self.width != None)
        doc = doc + ("width" -> self.width.get)
      if (self.heigth != None)
        doc = doc + ("heigth" -> self.heigth.get)
      if (self.pic != None)
        doc = doc + ("photo" -> self.pic.get)
      doc
    }
  }
  object WebPic {
    def fromDocument: Document => WebPic = doc => {
      WebPic(
        pid = doc.getString("pid"),
        seqno = doc.getInteger("seqno"),
        desc = mgoGetStringOrNone(doc,"desc"),
        width = mgoGetIntOrNone(doc,"width").asInstanceOf[Option[Int]],
        heigth = mgoGetIntOrNone(doc,"heigth").asInstanceOf[Option[Int]],
        pic = None
      )
    }
  }

The width,height field is the default width and height size provided by the client.Use the default width provided by the client in the database when submitting storage if the client does not provide a picture when requesting it.

There is also a count feature added to the repo that provides a pid that returns the number of pictures written under that pid name:

   import org.mongodb.scala.model.Filters._
    def count(pid: String):DBOResult[Int] = {
      val ctxCount = MGOContext(dbName = db,collName=coll)
        .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
        .setCommand(Count(filter=Some(equal("pid",pid))))
      mgoQuery[Int](ctxCount,None)
    }

The return type is DBResult[Int].There is also a feature to read the first WebPic record:

    def getOnePicture(pid: String, seqno: Int): DBOResult[R] = {
      val ctxFind = MGOContext(dbName = db, collName = coll)
        .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
        .setCommand(Find(filter = Some(and(equal("pid",pid),equal("seqno",seqno))), firstOnly = true))
      mgoQuery[R](ctxFind, converter)
    }

Note that this function returns a DBOResult[R] type.This is because we need to read the entire record, especially the width and height fields, to provide default values when the user does not specify a width or height.Because specific field names are involved, do a WebPic conversion when reading the Document:

    def getOneDocument(filtr: Bson): DBOResult[Document] = {
          val ctxFind = MGOContext(dbName = db,collName=coll)
         .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
         .setCommand(Find(filter = Some(filtr),firstOnly = true))
       mgoQuery[Document](ctxFind,None)
    }
    def getOnePicture(pid: String, seqno: Int): DBOResult[R] = {
      val ctxFind = MGOContext(dbName = db, collName = coll)
        .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
        .setCommand(Find(filter = Some(and(equal("pid",pid),equal("seqno",seqno))), firstOnly = true))
      mgoQuery[R](ctxFind, converter)
    }

To use getOnPicture, getOnDocument is generic.width,height is not recognized at compile time.

Okay, here are the modifications to the Route section.Beginning with a user submitting a picture storage request, the user may request a url in the following format:

(post & parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int]?)) such as:

Http://example.com:50081/public/gms/pictures?Pid=apple&width=128 The picture is placed in Entity of HttpRequest.

We need to get the number of apple s, seqno, store the information in the database, and return this seqno:

     pathPrefix("pictures") {
        (post &  parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int].?)) { (pid, optDesc, optWid, optHgh) =>
          val futCount: Future[Int] = repository.count(pid).value.value.runToFuture.map {
            eoi =>
              eoi match {
                case Right(oi) => oi match {
                  case Some(i) => i
                  case None => -1
                }
                case Left(err) => -1
              }
          }
          val count: Int = Await.result(futCount, 2 seconds)
          var doc = Document(
            "pid" -> pid,
            "seqno" -> count
          )
          if (optDesc != None)
            doc = doc + ("desc" -> optDesc.get)
          if (optWid != None)
            doc = doc + ("desc" -> optWid.get)
          if (optHgh != None)
            doc = doc + ("desc" -> optHgh.get)

          withoutSizeLimit {
            decodeRequest {
              extractDataBytes { bytes =>
                val fut = bytes.runFold(ByteString()) { case (hd, bs) =>
                  hd ++ bs
                }
                onComplete(fut) {
                  case Success(b) =>
                    doc = doc + ("pic" -> b.toArray)
                    val futmsg: Future[String] = repository.insert(doc).value.value.runToFuture.map {
                      eoc =>
                        eoc match {
                          case Right(oc) => oc match {
                            case Some(c) => count.toString //   c.toString()
                            case None => "insert may not complete!"
                          }
                          case Left(err) => err.getMessage
                        }
                    }
                    complete(futmsg)
                  case Failure(err) => complete(err)
                }
              }
            }
          }

Note that the results returned in Response must be of type ByteString.

Picture reading requests take two steps: first, provide pid to get a list of records without pictures (note pic=None in the fromDocument function of WebPic in Model), and return to the user, such as: http://example.com:50081/public/pms/pictures?pid=apple

The user gets the seqno in the list and assembles it into a complete url:http://example.com:50081/public/pms/pictures?Pid=apple&seqno=2&height=64

The system reads pictures and outputs pictures according to the user's width and height requirements or the default width and height data in the database:

        (get & parameters('pid, 'seqno.as[Int].?,'width.as[Int].?,'height.as[Int].?)) {
          (pid, optSeq, optWid,optHght) =>
            if (optSeq == None) {
              dbor = repository.query(equal("pid", pid))
              val futRows = dbor.value.value.runToFuture.map {
                eolr =>
                  eolr match {
                    case Right(olr) => olr match {
                      case Some(lr) => lr
                      case None => Seq[M]()
                    }
                    case Left(_) => Seq[M]()
                  }
              }
              complete(futureToJson(futRows))
            } else {
              val futOptPicRow: CancelableFuture[Option[WebPic]] = repository.getOnePicture(pid,optSeq.get)
                .value.value.runToFuture.map {
                eorow =>
                  eorow match {
                    case Right(orow) => orow match {
                      case Some(row) =>
                        if (row == null) None
                        else Some(row.asInstanceOf[WebPic])
                      case None => None
                    }
                    case Left(_) => None
                  }
              }
              onComplete(futOptPicRow) {
                case Success(optRow) => optRow match {
                  case Some(row) =>
                    val width  = if(optWid == None) row.width.getOrElse(128) else optWid.getOrElse(128)
                    val height = if(optHght == None) row.heigth.getOrElse(128) else optHght.getOrElse(128)
                    if (row.pic != None) {

                      withoutSizeLimit {
                        encodeResponseWith(Gzip) {
                          complete(
                            HttpEntity(
                              ContentTypes.`application/octet-stream`,
                              ByteArrayToSource(Imaging.setImageSize(row.pic.get.getData, width, height)
                              ))
                          )
                        }
                      }
                    } else complete(StatusCodes.NotFound)
                  case None => complete(StatusCodes.NotFound)
                }
                case Failure(err) => complete(err)
              }
            }
          }

Finally, we assemble this Route in the main route:

  implicit val webPicDao = new MongoRepo[WebPic]("testdb","pms", WebPic.fromDocument)
...

      pathPrefix("public") {
        (pathPrefix("crud")) {
          new MongoRoute[Person]("person")(personDao)
            .route ~
            new MongoRoute[Photo]("photo")(picDao)
              .route
        } ~
        new MongoRoute[WebPic]("pms")(webPicDao).route
      }

Here is the source code for this demonstration:

MongoModel.scala

package com.datatech.rest.mongo
import org.mongodb.scala._
import com.datatech.sdp.mongo.engine._
import MGOClasses._

object MongoModels {

  case class Person(
                   userid: String = "",
                   name: String = "",
                   age: Option[Int] = None,
                   dob: Option[MGODate] = None,
                   address: Option[String] = None
                   ) extends ModelBase[Document] {
    import org.mongodb.scala.bson._

    override def to: Document = {
      var doc = Document(
      "userid" -> this.userid,
      "name" -> this.name)


      if (this.age != None)
        doc = doc + ("age" -> this.age.get)

      if (this.dob != None)
        doc = doc + ("dob" -> this.dob.get)

      if (this.address != None)
        doc = doc + ("address" -> this.address.getOrElse(""))

      doc
    }

  }
  object Person {
    val fromDocument: Document => Person = doc => {
      val keyset = doc.keySet
      Person(
        userid = doc.getString("userid"),
        name = doc.getString("name"),
        age = mgoGetIntOrNone(doc,"age").asInstanceOf[Option[Int]],

        dob =  {if (keyset.contains("dob"))
          Some(doc.getDate("dob"))
        else None },

        address =  mgoGetStringOrNone(doc,"address")
      )
    }
  }

  case class Photo (
                     id: String,
                     loc: Option[String],
                     size: Option[Int],
                     credt: Option[MGODate],
                     photo: Option[MGOBlob]
                   ) extends ModelBase[Document] {
    override def to: Document = {
      var doc = Document("id" -> this.id)
      if (loc != None)
        doc = doc + ("loc" -> this.loc.get)
      if (size != None)
        doc = doc + ("size" -> this.size.get)
      if (credt != None)
        doc = doc + ("credt" -> this.credt.get)
      if (photo != None)
        doc = doc + ("photo" -> this.photo.get)
      doc
    }
  }

  object Photo {
    def fromDocument: Document => Photo = doc => {
      Photo(
        id = doc.getString("id"),
        loc = mgoGetStringOrNone(doc,"loc"),
        size = mgoGetIntOrNone(doc,"size").asInstanceOf[Option[Int]],
        credt = mgoGetDateOrNone(doc,"credt"),
        photo = mgoGetBlobOrNone(doc, "photo")
      )
    }
  }

  case class WebPic(
                     pid: String,
                     seqno: Int,
                     desc: Option[String],
                     width: Option[Int],
                     heigth: Option[Int],
                     pic: Option[MGOBlob]
                   ) extends ModelBase[Document] { self =>
    override def to: Document = {
      var doc = Document(
        "pid" -> self.pid,
        "seqno" -> self.seqno
      )
      if (self.desc != None)
        doc = doc + ("desc" -> self.desc.get)
      if (self.width != None)
        doc = doc + ("width" -> self.width.get)
      if (self.heigth != None)
        doc = doc + ("heigth" -> self.heigth.get)
      if (self.pic != None)
        doc = doc + ("photo" -> self.pic.get)
      doc
    }
  }
  object WebPic {
    def fromDocument: Document => WebPic = doc => {
      WebPic(
        pid = doc.getString("pid"),
        seqno = doc.getInteger("seqno"),
        desc = mgoGetStringOrNone(doc,"desc"),
        width = mgoGetIntOrNone(doc,"width").asInstanceOf[Option[Int]],
        heigth = mgoGetIntOrNone(doc,"heigth").asInstanceOf[Option[Int]],
        pic = None
      )
    }
  }
}

MongoRepo.scala

package com.datatech.rest.mongo
import org.mongodb.scala._
import org.bson.conversions.Bson
import org.mongodb.scala.result._
import com.datatech.sdp.mongo.engine._
import MGOClasses._
import MGOEngine._
import MGOCommands._
import com.datatech.sdp.result.DBOResult.DBOResult

object MongoRepo {

   class MongoRepo[R](db:String, coll: String, converter: Option[Document => R])(implicit client: MongoClient) {
    def getAll(next:Option[String],sort:Option[String],fields:Option[String],top:Option[Int]): DBOResult[Seq[R]] = {
      var res = Seq[ResultOptions]()
      next.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_FILTER,Some(Document(b)))}
      sort.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_SORT,Some(Document(b)))}
      fields.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_PROJECTION,Some(Document(b)))}
      top.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_LIMIT,None,b)}

      val ctxFind = MGOContext(dbName = db,collName=coll)
        .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
        .setCommand(Find(andThen = res))
      mgoQuery[Seq[R]](ctxFind,converter)
    }

     def query(filtr: Bson, next:Option[String]=None,sort:Option[String]=None,fields:Option[String]=None,top:Option[Int]=None): DBOResult[Seq[R]] = {
       var res = Seq[ResultOptions]()
       next.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_FILTER,Some(Document(b)))}
       sort.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_SORT,Some(Document(b)))}
       fields.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_PROJECTION,Some(Document(b)))}
       top.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_LIMIT,None,b)}
       val ctxFind = MGOContext(dbName = db,collName=coll)
         .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
         .setCommand(Find(filter = Some(filtr),andThen = res))
       mgoQuery[Seq[R]](ctxFind,converter)
    }

   import org.mongodb.scala.model.Filters._
    def count(pid: String):DBOResult[Int] = {
      val ctxCount = MGOContext(dbName = db,collName=coll)
        .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
        .setCommand(Count(filter=Some(equal("pid",pid))))
      mgoQuery[Int](ctxCount,None)
    }

    def getOneDocument(filtr: Bson): DBOResult[Document] = {
          val ctxFind = MGOContext(dbName = db,collName=coll)
         .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
         .setCommand(Find(filter = Some(filtr),firstOnly = true))
       mgoQuery[Document](ctxFind,None)
    }
    def getOnePicture(pid: String, seqno: Int): DBOResult[R] = {
      val ctxFind = MGOContext(dbName = db, collName = coll)
        .setActionType(MGO_ACTION_TYPE.MGO_QUERY)
        .setCommand(Find(filter = Some(and(equal("pid",pid),equal("seqno",seqno))), firstOnly = true))
      mgoQuery[R](ctxFind, converter)
    }
    def insert(doc: Document): DBOResult[Completed] = {
      val ctxInsert = MGOContext(dbName = db,collName=coll)
        .setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
        .setCommand(Insert(Seq(doc)))
      mgoUpdate[Completed](ctxInsert)
    }

    def delete(filter: Bson): DBOResult[DeleteResult] = {
      val ctxDelete = MGOContext(dbName = db,collName=coll)
        .setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
        .setCommand(Delete(filter))
      mgoUpdate[DeleteResult](ctxDelete)
    }

    def update(filter: Bson, update: Bson, many: Boolean): DBOResult[UpdateResult] = {
      val ctxUpdate = MGOContext(dbName = db,collName=coll)
        .setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
        .setCommand(Update(filter,update,None,!many))
      mgoUpdate[UpdateResult](ctxUpdate)
    }

    def replace(filter: Bson, row: Document): DBOResult[UpdateResult] = {
       val ctxUpdate = MGOContext(dbName = db,collName=coll)
         .setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
         .setCommand(Replace(filter,row))
       mgoUpdate[UpdateResult](ctxUpdate)
    }

  }

}

MongoRoute.scala

package com.datatech.rest.mongo
import akka.http.scaladsl.server.Directives
import com.datatech.sdp.file._

import scala.util._
import org.mongodb.scala._
import com.datatech.sdp.file.Streaming._
import org.mongodb.scala.result._
import MongoRepo._
import akka.stream.ActorMaterializer
import com.datatech.sdp.result.DBOResult._
import org.mongodb.scala.model.Filters._
import com.datatech.sdp.mongo.engine.MGOClasses._
import monix.execution.CancelableFuture
import akka.util._
import akka.http.scaladsl.model._
import akka.http.scaladsl.coding.Gzip
import com.datatech.rest.mongo.MongoModels.WebPic

import scala.concurrent._
import scala.concurrent.duration._
object MongoRoute {
  class MongoRoute[M <: ModelBase[Document]](val pathName: String)(repository: MongoRepo[M])(
    implicit c: MongoClient, m: Manifest[M], mat: ActorMaterializer) extends Directives with JsonConverter {
    import monix.execution.Scheduler.Implicits.global
    var dbor: DBOResult[Seq[M]] = _
    var dbou: DBOResult[UpdateResult] = _
    val route = pathPrefix(pathName) {
      pathPrefix("pictures") {
        (post &  parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int].?)) { (pid, optDesc, optWid, optHgh) =>
          val futCount: Future[Int] = repository.count(pid).value.value.runToFuture.map {
            eoi =>
              eoi match {
                case Right(oi) => oi match {
                  case Some(i) => i
                  case None => -1
                }
                case Left(err) => -1
              }
          }
          val count: Int = Await.result(futCount, 2 seconds)
          var doc = Document(
            "pid" -> pid,
            "seqno" -> count
          )
          if (optDesc != None)
            doc = doc + ("desc" -> optDesc.get)
          if (optWid != None)
            doc = doc + ("desc" -> optWid.get)
          if (optHgh != None)
            doc = doc + ("desc" -> optHgh.get)

          withoutSizeLimit {
            decodeRequest {
              extractDataBytes { bytes =>
                val fut = bytes.runFold(ByteString()) { case (hd, bs) =>
                  hd ++ bs
                }
                onComplete(fut) {
                  case Success(b) =>
                    doc = doc + ("pic" -> b.toArray)
                    val futmsg: Future[String] = repository.insert(doc).value.value.runToFuture.map {
                      eoc =>
                        eoc match {
                          case Right(oc) => oc match {
                            case Some(c) => count.toString //   c.toString()
                            case None => "insert may not complete!"
                          }
                          case Left(err) => err.getMessage
                        }
                    }
                    complete(futmsg)
                  case Failure(err) => complete(err)
                }
              }
            }
          }
        } ~
        (get & parameters('pid, 'seqno.as[Int].?,'width.as[Int].?,'height.as[Int].?)) {
          (pid, optSeq, optWid,optHght) =>
            if (optSeq == None) {
              dbor = repository.query(equal("pid", pid))
              val futRows = dbor.value.value.runToFuture.map {
                eolr =>
                  eolr match {
                    case Right(olr) => olr match {
                      case Some(lr) => lr
                      case None => Seq[M]()
                    }
                    case Left(_) => Seq[M]()
                  }
              }
              complete(futureToJson(futRows))
            } else {
              val futOptPicRow: CancelableFuture[Option[WebPic]] = repository.getOnePicture(pid,optSeq.get)
                .value.value.runToFuture.map {
                eorow =>
                  eorow match {
                    case Right(orow) => orow match {
                      case Some(row) =>
                        if (row == null) None
                        else Some(row.asInstanceOf[WebPic])
                      case None => None
                    }
                    case Left(_) => None
                  }
              }
              onComplete(futOptPicRow) {
                case Success(optRow) => optRow match {
                  case Some(row) =>
                    val width  = if(optWid == None) row.width.getOrElse(128) else optWid.getOrElse(128)
                    val height = if(optHght == None) row.heigth.getOrElse(128) else optHght.getOrElse(128)
                    if (row.pic != None) {

                      withoutSizeLimit {
                        encodeResponseWith(Gzip) {
                          complete(
                            HttpEntity(
                              ContentTypes.`application/octet-stream`,
                              ByteArrayToSource(Imaging.setImageSize(row.pic.get.getData, width, height)
                              ))
                          )
                        }
                      }
                    } else complete(StatusCodes.NotFound)
                  case None => complete(StatusCodes.NotFound)
                }
                case Failure(err) => complete(err)
              }
            }
          }
      } ~
      pathPrefix("blob") {
        (get & parameter('filter)) { filter =>
          val filtr = Document(filter)
          val futOptPic: CancelableFuture[Option[MGOBlob]] = repository.getOneDocument(filtr).value.value.runToFuture.map {
            eodoc =>
              eodoc match {
                case Right(odoc) => odoc match {
                  case Some(doc) =>
                    if (doc == null) None
                    else mgoGetBlobOrNone(doc, "photo")
                  case None => None
                }
                case Left(_) => None
              }
          }
          onComplete(futOptPic) {
            case Success(optBlob) => optBlob match {
              case Some(blob) =>
                withoutSizeLimit {
                  encodeResponseWith(Gzip) {
                    complete(
                      HttpEntity(
                        ContentTypes.`application/octet-stream`,
                        ByteArrayToSource(blob.getData)
                      )
                    )
                  }
                }
              case None => complete(StatusCodes.NotFound)
            }
            case Failure(err) => complete(err)
          }
        } ~
        (post &  parameter('bson)) { bson =>
          val bdoc = Document(bson)
          withoutSizeLimit {
            decodeRequest {
              extractDataBytes { bytes =>
                val fut = bytes.runFold(ByteString()) { case (hd, bs) =>
                  hd ++ bs
                }
                onComplete(fut) {
                  case Success(b) =>
                    val doc = bdoc + ("photo" -> b.toArray)
                    val futmsg = repository.insert(doc).value.value.runToFuture.map {
                      eoc =>
                        eoc match {
                          case Right(oc) => oc match {
                            case Some(c) => c.toString()
                            case None => "insert may not complete!"
                          }
                          case Left(err) => err.getMessage
                        }
                    }
                    complete(futmsg)
                  case Failure(err) => complete(err)
                }
              }
            }
          }
        }
      } ~
      (get & parameters('filter.?,'fields.?,'sort.?,'top.as[Int].?,'next.?)) {
        (filter,fields,sort,top,next) => {
        dbor = {
          filter match {
            case Some(fltr) => repository.query(Document(fltr),next,sort,fields,top)
            case None => repository.getAll(next,sort,fields,top)
          }
        }
        val futRows = dbor.value.value.runToFuture.map {
          eolr =>
            eolr match {
              case Right(olr) => olr match {
                case Some(lr) => lr
                case None => Seq[M]()
              }
              case Left(_) => Seq[M]()
            }
        }
        complete(futureToJson(futRows))
       }
      } ~ post {
        entity(as[String]) { json =>
          val extractedEntity: M = fromJson[M](json)
          val doc: Document = extractedEntity.to
          val futmsg = repository.insert(doc).value.value.runToFuture.map {
            eoc =>
              eoc match {
                case Right(oc) => oc match {
                  case Some(c) => c.toString()
                  case None => "insert may not complete!"
                }
                case Left(err) => err.getMessage
              }
          }

          complete(futmsg)
        }
      } ~ (put & parameter('filter,'set.?, 'many.as[Boolean].?)) { (filter, set, many) =>
        val bson = Document(filter)
        if (set == None) {
          entity(as[String]) { json =>
            val extractedEntity: M = fromJson[M](json)
            val doc: Document = extractedEntity.to
            val futmsg = repository.replace(bson, doc).value.value.runToFuture.map {
              eoc =>
                eoc match {
                  case Right(oc) => oc match {
                    case Some(d) => s"${d.getMatchedCount} matched rows, ${d.getModifiedCount} rows updated."
                    case None => "update may not complete!"
                  }
                  case Left(err) => err.getMessage
                }
            }
            complete(futureToJson(futmsg))
          }
        } else {
          set match {
            case Some(u) =>
              val ubson = Document(u)
              dbou = repository.update(bson, ubson, many.getOrElse(true))
            case None =>
              dbou = Left(new IllegalArgumentException("missing set statement for update!"))
          }
          val futmsg = dbou.value.value.runToFuture.map {
            eoc =>
              eoc match {
                case Right(oc) => oc match {
                  case Some(d) => s"${d.getMatchedCount} matched rows, ${d.getModifiedCount} rows updated."
                  case None => "update may not complete!"
                }
                case Left(err) => err.getMessage
              }
          }
          complete(futureToJson(futmsg))
        }
      } ~ (delete & parameters('filter, 'many.as[Boolean].?)) { (filter,many) =>
        val bson = Document(filter)
        val futmsg = repository.delete(bson).value.value.runToFuture.map {
          eoc =>
            eoc match {
              case Right(oc) => oc match {
                case Some(d) => s"${d.getDeletedCount} rows deleted."
                case None => "delete may not complete!"
              }
              case Left(err) => err.getMessage
            }
        }
        complete(futureToJson(futmsg))
      }
    }
  }

}

PMSServer.scala

package com.datatech.rest.mongo

import akka.actor._
import akka.stream._
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import pdi.jwt._
import AuthBase._
import MockUserAuthService._
import org.mongodb.scala._

import scala.collection.JavaConverters._
import MongoModels._
import MongoRepo._
import MongoRoute._


object PMSServer extends App {


  implicit val httpSys = ActorSystem("httpSystem")
  implicit val httpMat = ActorMaterializer()
  implicit val httpEC = httpSys.dispatcher

  val settings: MongoClientSettings = MongoClientSettings.builder()
    .applyToClusterSettings(b => b.hosts(List(new ServerAddress("localhost")).asJava))
    .build()
  implicit val client: MongoClient = MongoClient(settings)
  implicit val personDao = new MongoRepo[Person]("testdb","person", Some(Person.fromDocument))
  implicit val picDao = new MongoRepo[Photo]("testdb","photo", None)
  implicit val webPicDao = new MongoRepo[WebPic]("testdb","pms", WebPic.fromDocument)
  implicit val authenticator = new AuthBase()
    .withAlgorithm(JwtAlgorithm.HS256)
    .withSecretKey("OpenSesame")
    .withUserFunc(getValidUser)

  val route =
    path("auth") {
      authenticateBasic(realm = "auth", authenticator.getUserInfo) { userinfo =>
        post { complete(authenticator.issueJwt(userinfo))}
      }
    } ~
      pathPrefix("private") {
        authenticateOAuth2(realm = "private", authenticator.authenticateToken) { validToken =>
          FileRoute(validToken)
            .route
          // ~ ...
        }
      } ~
      pathPrefix("public") {
        (pathPrefix("crud")) {
          new MongoRoute[Person]("person")(personDao)
            .route ~
            new MongoRoute[Photo]("photo")(picDao)
              .route
        } ~
        new MongoRoute[WebPic]("pms")(webPicDao).route
      }

  val (port, host) = (50081,"192.168.11.189")

  val bindingFuture = Http().bindAndHandle(route,host,port)

  println(s"Server running at $host $port. Press any key to exit ...")

  scala.io.StdIn.readLine()


  bindingFuture.flatMap(_.unbind())
    .onComplete(_ => httpSys.terminate())


}

imaging.scala

package com.datatech.sdp.file
import javax.imageio.ImageIO
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream

object Imaging {
  def setImageSize(barr: Array[Byte], wth: Int, hth: Int): Array[Byte] = {
    val input = ImageIO.read(new ByteArrayInputStream(barr))
    val image = new BufferedImage(wth, hth, BufferedImage.TYPE_INT_BGR)
    val g = image.getGraphics.asInstanceOf[Graphics2D]
    g.drawImage(input, 0, 0, wth, hth, null) //Drawing
    g.dispose()
    image.flush()
    val barros = new ByteArrayOutputStream()
    ImageIO.write(image, "jpg", barros)
    barr
  }
}

Tags: Scala MongoDB REST Database

Posted on Wed, 21 Aug 2019 18:49:20 -0700 by warrior rabbit