def allMetadataFor()

in app/models/ProjectMetadata.scala [82:136]


  def allMetadataFor(projectRef:Int)(implicit db:slick.jdbc.PostgresProfile#Backend#Database):Future[Try[Seq[ProjectMetadata]]] =
    db.run(
      TableQuery[ProjectMetadataRow].filter(_.projectRef===projectRef).result.asTry
    )

  def deleteAllMetadataFor(projectRef:Int)(implicit db:slick.jdbc.PostgresProfile#Backend#Database):Future[Try[Int]] =
    db.run(
      TableQuery[ProjectMetadataRow].filter(_.projectRef===projectRef).delete.asTry
    )

  /**
    * Set (by upsert) entries in bulk
    * @param projectRef project ID to set entries for
    * @param data a Map[String,String] containing keys and values to set
    * @param db implicitly provided database object
    * @return a Future, containing an Int indicating the number of insert/updates. The future will fail if there is a database error
    */
  def setBulk(projectRef:Int, data:Map[String,String])(implicit db:slick.jdbc.PostgresProfile#Backend#Database):Future[Try[Int]] = {
    def tryInsertWithRecovery(mdEntry:ProjectMetadata,onRetry:Boolean=false):Future[ProjectMetadata] = mdEntry.save.flatMap({
      case Failure(err)=>
        val errorString = err.toString
        if (errorString.contains("violates foreign key constraint") ||
          errorString.contains("Referential integrity constraint violation") ||
          errorString.contains("violates unique constraint")) {
          ProjectMetadata.deleteFor(mdEntry.projectRef, mdEntry.key).flatMap({
            case Failure(err)=>
              logger.error("Could not delete old metadata value", err)
              throw err
            case Success(rows)=>
              if (onRetry)
                throw err
              else
                tryInsertWithRecovery(mdEntry, onRetry = true)
          })

        } else {
          throw err
        }
      case Success(savedEntry)=>Future(savedEntry)
    })

    val createdObjects = Future.sequence(data.map(kvTuple=>ProjectMetadata.getOrCreate(projectRef,kvTuple._1,Some(kvTuple._2))))
    val splitResultsFuture = createdObjects.map(_.partition(_.isSuccess))

    splitResultsFuture.flatMap(resultTuple=>{
      if(resultTuple._2.count(x=>true)>0){
        val resultSeq = resultTuple._2.foldLeft("")((acc, failedTry)=>acc + failedTry.failed.get.toString).mkString("; ")
        Future(Failure(new RuntimeException(resultSeq)) ) //fixme: define a custom exception to hold the sequence instead
      } else {
        Future.sequence(resultTuple._1.map(successfulTry=>tryInsertWithRecovery(successfulTry.get)))
          .map(iterable=>Success(iterable.count(x=>true)))
          .recover({ case ex:Throwable=>Failure(ex) })
      }
    })
  }