Skip to content

Commit

Permalink
update error handling, test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Hunt committed Mar 19, 2017
1 parent 2492e6e commit f257831
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 60 deletions.
62 changes: 41 additions & 21 deletions src/main/scala/com/cornfluence/proteus/ArangoClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ object ArangoClient {
new ArangoClient(host, port, https, databaseName)
}

/**
* Handles authentication using JWT headers
*/
trait Auth extends ArangoConfig {

private val logger = Logger("Auth")
Expand All @@ -41,14 +44,14 @@ trait Auth extends ArangoConfig {
}

private def authToken(request: HttpRequest, jwt: String): HttpRequest =
request.header("Authorization",s"bearer $jwt")
request.header("Authorization", s"bearer $jwt")

case class Auth(username: String, password: String)
case class Jwt(jwt: String, must_change_password: Boolean)
}

/**
* General Database management methods
* General Database management
*
* @param host
* @param port
Expand All @@ -73,9 +76,11 @@ class ArangoClient(host: String = "localhost", port: Int = 8529, https: Boolean
}


/*
Retrieves the list of all existing databases
*/
/**
* Retrieves the list of all existing databases
*
* @return
*/
def getDatabaseList: Future[Either[Throwable, List[DatabaseName]]] = Future {
val response: HttpResponse[String] = auth(Http(s"$arangoHost/$api/$database")).asString
decode[ResultList](response.body) match {
Expand All @@ -86,61 +91,76 @@ class ArangoClient(host: String = "localhost", port: Int = 8529, https: Boolean
}
}

/*
Creates a new database
*/
/**
* Creates a new database
*
* @param dbName
* @param users
* @return
*/
def createDatabase(dbName: String, users: Option[List[User]]): Future[Either[Throwable, Unit]] = Future {
val postData = Database(dbName, users)
val response: HttpResponse[String] = auth(Http(s"$arangoHost/$api/$database").postData(postData.asJson.noSpaces)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error("ArangoClient.createDatabase", error.getMessage)
Left(error)
}
}

/*
Deletes the database along with all data stored in it
*/
/**
* Deletes the database along with all data stored in it
*
* @param dbName
* @return
*/
def deleteDatabase(dbName: String): Future[Either[Throwable, Unit]] = Future {
val response: HttpResponse[String] = auth(Http(s"$arangoHost/$api/$database/$dbName").method(DELETE)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error("ArangoClient.deleteDatabase", error.getMessage)
Left(error)
}
}

/*
Creates a new collection
*/
/**
* Creates a new collection
*
* @param dbName
* @param collectionName
* @return
*/
def createCollection(dbName: String, collectionName: String): Future[Either[Throwable, CollectionResponse]] = Future {
val postData = Collection(collectionName)
val response: HttpResponse[String] = auth(Http(s"$arangoHost/$db/$dbName/$api/collection").postData(postData.asJson.noSpaces)).asString
decode[CollectionResponse](response.body) match {
case Right(ok) =>
if(ok.error) Left(new Exception(errorMessage(ok.errorMessage)))
if(ok.error) error(errorMessage(ok.errorMessage))
else Right(ok)
case Left(error) =>
logger.error("ArangoClient.createCollection", error.getMessage)
Left(error)
}
}

/*
Drops a collection
*/
/**
* Drops a collection
*
* @param dbName
* @param collectionName
* @return
*/
def dropCollection(dbName: String, collectionName: String): Future[Either[Throwable, CollectionResponse]] = Future {
val response: HttpResponse[String] = auth(Http(s"$arangoHost/$db/$dbName/$api/collection/$collectionName").method(DELETE)).asString
decode[CollectionResponse](response.body) match {
case Right(ok) =>
if(ok.error) Left(new Exception(errorMessage(ok.errorMessage)))
if(ok.error) error(errorMessage(ok.errorMessage))
else Right(ok)
case Left(error) =>
logger.error("ArangoClient.dropCollection", error.getMessage)
Expand Down
14 changes: 11 additions & 3 deletions src/main/scala/com/cornfluence/proteus/DocumentClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ object DocumentClient {
new DocumentClient(host, port, https, databaseName)
}

/**
* Manages Document API operations
*
* @param host
* @param port
* @param https
* @param databaseName
*/
class DocumentClient(host: String = "localhost", port: Int = 8529, https: Boolean = false, databaseName: String)
extends ArangoClient(host, port, https, databaseName) with Auth {

Expand All @@ -36,7 +44,7 @@ class DocumentClient(host: String = "localhost", port: Int = 8529, https: Boolea
val response = auth(Http(s"$arangoHost/$db/$dbName/$api/document/$collectionName").postData(documentString)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else ok._key.toRight[Throwable](new Exception("Document key is missing"))
case Left(error) =>
logger.error(error.getMessage)
Expand All @@ -58,7 +66,7 @@ class DocumentClient(host: String = "localhost", port: Int = 8529, https: Boolea
val response = auth(Http(s"$arangoHost/$db/$dbName/$api/document/$collectionName/$documentID").put(documentString)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else ok._key.toRight[Throwable](new Exception("Document key is missing"))
case Left(error) =>
logger.error(error.getMessage)
Expand Down Expand Up @@ -109,7 +117,7 @@ class DocumentClient(host: String = "localhost", port: Int = 8529, https: Boolea
val response = auth(Http(s"$arangoHost/$db/$dbName/$api/document/$collectionName/$documentID").method(DELETE)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error(error.getMessage)
Expand Down
28 changes: 18 additions & 10 deletions src/main/scala/com/cornfluence/proteus/GraphClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ object GraphClient {
new GraphClient(hostMachine, port, https, databaseName)
}

/**
* Manages Graph API operations
*
* @param hostMachine
* @param port
* @param https
* @param databaseName
*/
class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Boolean = false, databaseName: String)
extends ArangoClient(hostMachine, port, https, databaseName) with Auth {

Expand All @@ -35,7 +43,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial").postData(Graph(graphName, edges).asJson.noSpaces)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(s"Error creating graph with code ${ok.code}"))
if(isError(ok)) error(s"Error creating graph with code ${ok.code}")
else ok.graph.toRight[Throwable](new Exception("Graph response missing"))
case Left(error) =>
logger.error("GraphClient.createGraph", error.getMessage)
Expand All @@ -53,7 +61,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName").method(DELETE)).asString
decode[DropGraphResponse](response.body) match {
case Right(ok) =>
if(ok.error) Left(new Exception(s"Error dropping graph with code ${ok.code}"))
if(ok.error) error(s"Error dropping graph with code ${ok.code}")
else Right(ok.removed)
case Left(error) =>
logger.error("GraphClient.dropGraph", error.getMessage)
Expand All @@ -76,7 +84,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/vertex").postData(collection.asJson.noSpaces)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else ok.graph.toRight[Throwable](new Exception("Graph reesponse missing"))
case Left(error) =>
logger.error("GraphClient.createVertexCollection", error.getMessage)
Expand All @@ -101,7 +109,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/vertex/$vertexCollection").postData(json)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else ok.vertex.toRight[Throwable](new Exception("Vertex missing from response"))
case Left(error) =>
logger.error("GraphClient.createVertex", error.getMessage)
Expand All @@ -126,7 +134,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/edge/").postData(edge)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else ok.graph.map(_.edgeDefinitions).toRight[Throwable](new Exception("Edge definition response missing"))
case Left(error) =>
logger.error("GraphClient.createEdgeCollection", error.getMessage)
Expand Down Expand Up @@ -155,7 +163,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/edge/$collectionName").postData(edge)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else ok.edge.toRight[Throwable](new Exception("Edge response missing"))
case Left(error) =>
logger.error("GraphClient.createEdge", error.getMessage)
Expand All @@ -175,7 +183,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/edge/$collectionName/$edgeKey").method(DELETE)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error("GraphClient.deleteEdge", error.getMessage)
Expand All @@ -195,7 +203,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/vertex/$collectionName/$vertexKey").method(DELETE)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error("GraphClient.deleteVertex", error.getMessage)
Expand All @@ -215,7 +223,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/edge/$collectionName").method(DELETE)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error("GraphClient.deleteEdgeCollection", error.getMessage)
Expand All @@ -234,7 +242,7 @@ class GraphClient(hostMachine: String = "localhost", port: Int = 8529, https: Bo
val response = auth(Http(s"$arangoHost/$api/$gharial/$graphName/vertex/$collectionName").method(DELETE)).asString
decode[ResultMessage](response.body) match {
case Right(ok) =>
if(ok.error.getOrElse(false)) Left(new Exception(errorMessage(ok.errorMessage)))
if(isError(ok)) error(errorMessage(ok.errorMessage))
else Right(())
case Left(error) =>
logger.error("GraphClient.deleteVertexCollection", error.getMessage)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/com/cornfluence/proteus/HTTP.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ trait HTTP {

def handleResponse[T](response: HttpResponse[String]): Either[ResultMessage, HttpResponse[String]] = {
response.code match {
case c if c.toString.startsWith("2") => Right(response)
case code if code.toString.startsWith("2") => Right(response)
case _ =>
decode[ResultMessage](response.body) match {
case Right(ok) => Left(ok)
Expand Down
14 changes: 10 additions & 4 deletions src/main/scala/com/cornfluence/proteus/package.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.cornfluence

import com.cornfluence.proteus.models.ResultMessage

import scala.concurrent.ExecutionContextExecutorService

package object proteus {

val api = "_api"
Expand All @@ -8,10 +12,12 @@ package object proteus {

val DELETE = "DELETE"

def errorMessage(message: Option[String]) = {
message.getOrElse("")
}
def errorMessage(message: Option[String]): String = message.getOrElse("")

def error(message: String) = Left(new Exception(message))

def isError(result: ResultMessage): Boolean = result.error.getOrElse(false)

implicit val proteusEC = new ProteusExecutionContext().ec
implicit val proteusEC: ExecutionContextExecutorService = new ProteusExecutionContext().ec

}
31 changes: 20 additions & 11 deletions src/test/scala/proteus/ArangoClientTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@ class ArangoClientTest extends FunSpec {

describe("==============\n| Arango Client Test |\n==============") {

// describe("Authenticate with Arango") {
// it("should return a JWT") {
// val result = driver.auth("root", "")
// val res = Await.result(result, 5 second)
// res match {
// case Left(err) => fail(err.getMessage)
// case Right(ok) => ok
// }
// }
// }

describe("Create Database") {
it("should create new Database") {
val result = driver.createDatabase(testDB, Some(List(User("user", "password"))))
Expand All @@ -40,6 +29,26 @@ class ArangoClientTest extends FunSpec {
}
}

describe("Get Databases") {
it("should properly retrieve all databases") {
val result = driver.getDatabaseList
Await.result(result, 5.seconds) match {
case Left(err) => fail(err.getMessage)
case Right(ok) => assert(ok.nonEmpty)
}
}
}

describe("Get current database") {
it("should properly retrieve the current database") {
val result = driver.getCurrentDatabase
Await.result(result, 5.seconds) match {
case Left(err) => fail(err.getMessage)
case Right(ok) => assert(ok.result.name.nonEmpty)
}
}
}

describe("Delete Database") {
it("should delete the Database") {
val result = driver.deleteDatabase(testDB)
Expand Down
12 changes: 2 additions & 10 deletions src/test/scala/proteus/DocumentClientTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class DocumentClientTest extends FunSpec {
val driver = DocumentClient(name = testDB)

describe("==============\n| Document Client Test |\n==============") {

describe("Create Database") {
it("should create new Database") {
val result = driver.createDatabase(testDB, Some(List(User("charles", "password"))))
Expand All @@ -26,15 +27,6 @@ class DocumentClientTest extends FunSpec {
}
}
}
describe("Get Databases") {
it("should properly retrieve all databases") {
val result = driver.getDatabaseList
Await.result(result, 5.seconds) match {
case Left(err) => fail(err.getMessage)
case Right(ok) => assert(ok.nonEmpty)
}
}
}

describe("Create Collection") {
it("should create a collection") {
Expand Down Expand Up @@ -113,7 +105,7 @@ class DocumentClientTest extends FunSpec {
val res = Await.result(result, 5 second)
res match {
case Left(err) => fail(err)
case Right(ok) => ok should include("success")
case Right(ok) => ok
}
}
}
Expand Down

0 comments on commit f257831

Please sign in to comment.