Skip to content

Commit

Permalink
Merge bb3b266 into 8f2c5f3
Browse files Browse the repository at this point in the history
  • Loading branch information
David Shiga committed May 8, 2015
2 parents 8f2c5f3 + bb3b266 commit 11289d3
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,6 @@ import spray.httpx.unmarshalling.Unmarshaller
import scala.annotation.meta.field

object AgoraEntity {
implicit val AgoraEntityUnmarshaller =
Unmarshaller[AgoraEntity](`application/json`) {
case HttpEntity.NonEmpty(contentType, data) grater[AgoraEntity].fromJSON(data.asString)
case HttpEntity.Empty new AgoraEntity()
}

implicit val AgoraEntityMarshaller =
Marshaller.of[AgoraEntity](`application/json`) { (value, contentType, context) =>
context.marshalTo(HttpEntity(contentType, grater[AgoraEntity].toCompactJSON(value)))
}

def fromAgoraAddRequest(agoraAddRequest: AgoraAddRequest, createDate: Option[Date]) = {
new AgoraEntity(namespace = Option(agoraAddRequest.namespace),
name = Option(agoraAddRequest.name),
Expand All @@ -35,7 +24,16 @@ object AgoraEntity {
payload = Option(agoraAddRequest.payload)
)
}


def fromAgoraSearch(agoraSearch: AgoraSearch) = {
new AgoraEntity(namespace = agoraSearch.namespace,
name = agoraSearch.name,
id = agoraSearch.id,
synopsis = agoraSearch.synopsis,
documentation = agoraSearch.documentation,
owner = agoraSearch.owner,
payload = agoraSearch.payload)
}
}

object AgoraAddRequest {
Expand Down Expand Up @@ -69,6 +67,23 @@ case class AgoraEntity(@(ApiModelProperty@field)(required = true, value = "The n
payload: Option[String] = None
)

@ApiModel(value = "Agora Search")
case class AgoraSearch(@(ApiModelProperty@field)(required = true, value = "The namespace to which the method belongs")
namespace: Option[String] = None,
@(ApiModelProperty@field)(required = true, value = "The method name ")
name: Option[String] = None,
@(ApiModelProperty@field)(required = true, value = "The method id")
var id: Option[Int] = None,
@(ApiModelProperty@field)(required = true, value = "User who owns this method in the methods repo")
owner: Option[String] = None,
@(ApiModelProperty@field)(required = true, value = "A short description of the method")
synopsis: Option[String] = None,
@(ApiModelProperty@field)(required = true, value = "Method documentation")
documentation: Option[String] = None,
@(ApiModelProperty@field)(required = true, value = "The method payload")
payload: Option[String] = None
)

@ApiModel(value = "Request to add method")
case class AgoraAddRequest(@(ApiModelProperty@field)(required = true, value = "The namespace to which the method belongs")
namespace: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.broadinstitute.dsde.agora.server.webservice.methods

import akka.actor.Actor
import org.broadinstitute.dsde.agora.server.dataaccess.AgoraDao
import org.broadinstitute.dsde.agora.server.model.AgoraEntity
import org.broadinstitute.dsde.agora.server.model.{AgoraSearch, AgoraEntity}
import org.broadinstitute.dsde.agora.server.webservice.util.ServiceMessages
import org.broadinstitute.dsde.agora.server.webservice.PerRequest._
import spray.http.StatusCodes._
Expand All @@ -11,17 +11,19 @@ import com.novus.salat._
import com.novus.salat.global._



/**
* Actor responsible for querying the methods repository.
*/
class MethodsQueryHandler extends Actor {
implicit val system = context.system

def receive = {
case ServiceMessages.Query(requestContext: RequestContext, namespace: String, name: String, id: Int) =>
case ServiceMessages.QueryByNamespaceNameId(requestContext: RequestContext, namespace: String, name: String, id: Int) =>
query(requestContext, namespace, name, id)
context.stop(self)
case ServiceMessages.Query(requestContext: RequestContext, agoraSearch: AgoraSearch) =>
query(requestContext, agoraSearch)
context.stop(self)
}

def query(requestContext: RequestContext, namespace: String, name: String, id: Int): Unit = {
Expand All @@ -30,4 +32,9 @@ class MethodsQueryHandler extends Actor {
case Some(method) => context.parent ! grater[AgoraEntity].toJSON(method)
}
}

def query(requestContext: RequestContext, agoraSearch: AgoraSearch): Unit = {
val entities = AgoraDao.createAgoraDao.find(AgoraEntity.fromAgoraSearch(agoraSearch))
context.parent ! grater[AgoraEntity].toJSONArray(entities)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.broadinstitute.dsde.agora.server.webservice.methods

import com.wordnik.swagger.annotations._
import org.broadinstitute.dsde.agora.server.model.{AgoraAddRequest, AgoraEntity}
import org.broadinstitute.dsde.agora.server.model.{AgoraSearch, AgoraAddRequest, AgoraEntity}
import org.broadinstitute.dsde.agora.server.webservice.util.{ApiUtil, ServiceHandlerProps, ServiceMessages}
import org.broadinstitute.dsde.agora.server.webservice.PerRequestCreator
import spray.routing.HttpService
Expand All @@ -10,9 +10,9 @@ import spray.routing.HttpService
trait MethodsService extends HttpService with PerRequestCreator {
this: ServiceHandlerProps => // Require a concrete ServiceHandlerProps creator to be mixed in

val routes = queryRoute ~ postRoute
val routes = queryByNamespaceNameIdRoute ~ queryRoute ~ postRoute

@ApiOperation(value = "Query a method from the method repository by namespace, name, and id",
@ApiOperation(value = "Get a method in the method repository matching namespace, name, and id",
nickname = "methods",
httpMethod = "GET",
produces = "application/json",
Expand All @@ -28,11 +28,40 @@ trait MethodsService extends HttpService with PerRequestCreator {
new ApiResponse(code = 404, message = "Method Not Found"),
new ApiResponse(code = 500, message = "Internal Error")
))
def queryRoute =
def queryByNamespaceNameIdRoute =
path(ApiUtil.Methods.path / Segment / Segment / Segment) { (namespace, name, id) =>
get {
requestContext =>
perRequest(requestContext, methodsQueryHandlerProps, ServiceMessages.Query(requestContext, namespace, name, id.toInt))
perRequest(requestContext, methodsQueryHandlerProps, ServiceMessages.QueryByNamespaceNameId(requestContext, namespace, name, id.toInt))
}
}

@ApiOperation(value = "Query for methods in the method repository",
nickname = "methods",
httpMethod = "GET",
produces = "application/json",
response = classOf[Seq[AgoraEntity]],
notes = "API is rapidly changing.")
@ApiImplicitParams(Array(
new ApiImplicitParam(name = "namespace", required = true, dataType = "string", paramType = "path", value = "Namespace"),
new ApiImplicitParam(name = "name", required = true, dataType = "string", paramType = "path", value = "Name"),
new ApiImplicitParam(name = "id", required = true, dataType = "string", paramType = "path", value = "Id"),
new ApiImplicitParam(name = "synopsis", required = true, dataType = "string", paramType = "path", value = "Synopsis"),
new ApiImplicitParam(name = "documentation", required = true, dataType = "string", paramType = "path", value = "Documentation"),
new ApiImplicitParam(name = "owner", required = true, dataType = "string", paramType = "path", value = "Owner"),
new ApiImplicitParam(name = "payload", required = true, dataType = "string", paramType = "path", value = "Payload")
))
@ApiResponses(Array(
new ApiResponse(code = 200, message = "Successful Request"),
new ApiResponse(code = 500, message = "Internal Error")
))
def queryRoute =
path(ApiUtil.Methods.path) {
get {
parameters('namespace?, 'name?, 'id.as[Int]?, 'synopsis?, 'documentation?, 'owner?, 'payload?).as(AgoraSearch) { agoraSearch =>
requestContext =>
perRequest(requestContext, methodsQueryHandlerProps, ServiceMessages.Query(requestContext, agoraSearch))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package org.broadinstitute.dsde.agora.server.webservice.util

import org.broadinstitute.dsde.agora.server.model.AgoraAddRequest
import org.broadinstitute.dsde.agora.server.model.{AgoraSearch, AgoraEntity, AgoraAddRequest}
import spray.routing.RequestContext

/**
* Case classes representing messages to pass to service handler actors
*/
object ServiceMessages {
case class Query(requestContext: RequestContext, namespace: String, name: String, id: Int)

case class QueryByNamespaceNameId(requestContext: RequestContext, namespace: String, name: String, id: Int)

case class Query(requestContext: RequestContext, agoraSearch: AgoraSearch)

case class Add(requestContext: RequestContext, agoraAddRequest: AgoraAddRequest)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ package org.broadinstitute.dsde.agora.server
import org.broadinstitute.dsde.agora.server.model.{AgoraAddRequest, AgoraEntity}

trait AgoraTestData {
val namespace = "broad"

val name = "testMethod"

val namespace1 = "broad"
val namespace2 = "hellbender"
val name1 = "testMethod1"
val name2 = "testMethod2"
val synopsis = "This is a test method"

val documentation = "This is the documentation"

val owner = "bob the builder"

val owner1 = "bob"
val owner2 = "dave"
val payload = """task wc {
| command {
| echo "${str}" | wc -c
Expand All @@ -29,25 +28,28 @@ trait AgoraTestData {
| }
|}""".stripMargin

val testEntity = AgoraEntity(namespace = Option(namespace), name = Option(name))
val testEntity1 = AgoraEntity(namespace = Option(namespace1), name = Option(name1), owner = Option(owner1))
val testEntity2 = AgoraEntity(namespace = Option(namespace1), name = Option(name2), owner = Option(owner1))
val testEntity3 = AgoraEntity(namespace = Option(namespace2), name = Option(name1), owner = Option(owner1))
val testEntity4 = AgoraEntity(namespace = Option(namespace1), name = Option(name1), owner = Option(owner2))

val testAddRequest = new AgoraAddRequest(
namespace = namespace,
name = name,
namespace = namespace1,
name = name1,
synopsis = synopsis,
documentation = documentation,
owner = owner,
owner = owner1,
payload = payload
)

val badPayload = "task test {"

val testBadAddRequest = new AgoraAddRequest(
namespace = namespace,
name = name,
namespace = namespace1,
name = name1,
synopsis = synopsis,
documentation = documentation,
owner = owner,
owner = owner1,
payload = badPayload
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,30 @@ import org.scalatest.{DoNotDiscover, FlatSpec}
class MethodsDbTest extends FlatSpec with AgoraDbTest with AgoraTestData {

"Agora" should "be able to store a method" in {
agoraDao.insert(testEntity)
agoraDao.insert(testEntity1)

val entity = agoraDao.findSingle(testEntity).get
val entity = agoraDao.findSingle(testEntity1).get

assert(entity == testEntity)
assert(entity == testEntity1)
}

"Agora" should "be able to query by namespace, name and version and get back a single entity" in {
//NB: agoraTestMethod has already been stored.
val queryEntity = new AgoraEntity(namespace = Option(namespace), name = Option(name), id = testEntity.id)
val queryEntity = new AgoraEntity(namespace = Option(namespace1), name = Option(name1), id = testEntity1.id)

val entity = agoraDao.findSingle(queryEntity).get

assert(entity == testEntity)
assert(entity == testEntity1)
}

"Agora" should "increment the id number if we insert the same namespace/name entity" in {
agoraDao.insert(testEntity)
agoraDao.insert(testEntity1)

val previousVersionEntity = testEntity.copy()
previousVersionEntity.id = Option(testEntity.id.get - 1)
val previousVersionEntity = testEntity1.copy()
previousVersionEntity.id = Option(testEntity1.id.get - 1)

val entity1 = agoraDao.findSingle(previousVersionEntity).get
val entity2 = agoraDao.findSingle(testEntity).get
val entity2 = agoraDao.findSingle(testEntity1).get

assert(entity1.id.get == entity2.id.get - 1)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.broadinstitute.dsde.agora.server.webservice

import org.broadinstitute.dsde.agora.server.model.AgoraEntity
import org.broadinstitute.dsde.agora.server.model.AgoraEntity._
import org.broadinstitute.dsde.agora.server.webservice.methods.MethodsService
import org.broadinstitute.dsde.agora.server.webservice.util.{ApiUtil, ServiceHandlerProps}
import org.broadinstitute.dsde.agora.server.{AgoraDbTest, AgoraTestData}
Expand All @@ -10,6 +9,8 @@ import spray.http.StatusCodes._
import spray.httpx.marshalling._
import spray.routing.Directives
import spray.testkit.ScalatestRouteTest
import com.novus.salat._
import com.novus.salat.global._

@DoNotDiscover
class ApiServiceSpec extends FlatSpec with Matchers with Directives with ScalatestRouteTest with AgoraTestData with AgoraDbTest {
Expand All @@ -21,22 +22,36 @@ class ApiServiceSpec extends FlatSpec with Matchers with Directives with Scalate
val methodsService = new MethodsService with ActorRefFactoryContext with ServiceHandlerProps

"Agora" should "return information about a method, including metadata " in {
val insertedEntity = agoraDao.insert(testEntity)
val insertedEntity = agoraDao.insert(testEntity1)

Get(ApiUtil.Methods.withLeadingSlash + "/" + namespace + "/" + name + "/"
+ insertedEntity.id.get) ~> methodsService.queryRoute ~> check {
responseAs[AgoraEntity] === insertedEntity
Get(ApiUtil.Methods.withLeadingSlash + "/" + namespace1 + "/" + name1 + "/"
+ insertedEntity.id.get) ~> methodsService.queryByNamespaceNameIdRoute ~> check {
val rawResponse = responseAs[String]
val response = grater[AgoraEntity].fromJSONArray(rawResponse)
response === insertedEntity
}
}

"Agora" should "return methods matching the query" in {
agoraDao.insert(testEntity2)
agoraDao.insert(testEntity3)
agoraDao.insert(testEntity4)
Get(ApiUtil.Methods.withLeadingSlash + "?namespace=" + namespace1 + "&owner=" + owner1) ~> methodsService.queryRoute ~> check {
val rawResponse = responseAs[String]
val response = grater[AgoraEntity].fromJSONArray(rawResponse)
response === Seq(testEntity1, testEntity2)
}
}

"Agora" should "create and return a method" in {
Post(ApiUtil.Methods.withLeadingSlash, marshal(testAddRequest)) ~> methodsService.postRoute ~> check {
val response = responseAs[AgoraEntity]
response.namespace === namespace
response.name === name
val rawResponse = responseAs[String]
val response = grater[AgoraEntity].fromJSON(rawResponse)
response.namespace === namespace1
response.name === name1
response.synopsis === synopsis
response.documentation === documentation
response.owner === owner
response.owner === owner1
response.payload === payload
response.id === 1
response.createDate != null
Expand Down

0 comments on commit 11289d3

Please sign in to comment.