Skip to content

Commit

Permalink
GAWB-849: ga4gh api for agora. (#218)
Browse files Browse the repository at this point in the history
* ga4gh APIs
  • Loading branch information
davidangb authored and rushtong committed Oct 16, 2017
1 parent 6c8af23 commit ce8429f
Show file tree
Hide file tree
Showing 11 changed files with 1,300 additions and 131 deletions.
430 changes: 429 additions & 1 deletion src/main/resources/swagger/agora.yaml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.broadinstitute.dsde.agora.server.ga4gh

import akka.pattern.pipe
import org.broadinstitute.dsde.agora.server.dataaccess.permissions.{AccessControl, PermissionsDataSource}
import org.broadinstitute.dsde.agora.server.exceptions.ValidationException
import org.broadinstitute.dsde.agora.server.ga4gh.Ga4ghServiceMessages._
import org.broadinstitute.dsde.agora.server.ga4gh.Models._
import org.broadinstitute.dsde.agora.server.model.{AgoraEntity, AgoraEntityType}
import org.broadinstitute.dsde.agora.server.webservice.PerRequest.{PerRequestMessage, RequestComplete}
import org.broadinstitute.dsde.agora.server.webservice.handlers.QueryHandler
import spray.httpx.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._
import spray.routing.RequestContext

import scala.concurrent.{ExecutionContext, Future}

class Ga4ghQueryHandler(dataSource: PermissionsDataSource, override implicit val ec: ExecutionContext)
extends QueryHandler(dataSource, ec) {

override def receive: akka.actor.Actor.Receive = {
case QueryPublicSingle(requestContext: RequestContext, entity: AgoraEntity) =>
queryPublicSingle(requestContext, entity) pipeTo context.parent

case QueryPublicSinglePayload(requestContext: RequestContext, entity: AgoraEntity, descriptorType: ToolDescriptorType.DescriptorType) =>
queryPublicSinglePayload(requestContext, entity, descriptorType) pipeTo context.parent

case QueryPublic(requestContext: RequestContext, agoraSearch: AgoraEntity) =>
queryPublic(requestContext, agoraSearch) pipeTo context.parent

case QueryPublicTool(requestContext: RequestContext, agoraSearch: AgoraEntity) =>
queryPublicTool(requestContext, agoraSearch) pipeTo context.parent

case QueryPublicTools(requestContext: RequestContext) =>
queryPublicTools(requestContext) pipeTo context.parent


}

def queryPublicSingle(requestContext: RequestContext, entity: AgoraEntity): Future[PerRequestMessage] = {
val entityTypes = Seq(entity.entityType.getOrElse(throw ValidationException("need an entity type")))
agoraBusiness.findSingle(entity, entityTypes, AccessControl.publicUser) map { foundEntity =>
RequestComplete(ToolVersion(foundEntity))
}
}

def queryPublicSinglePayload(requestContext: RequestContext, entity: AgoraEntity, descriptorType: ToolDescriptorType.DescriptorType): Future[PerRequestMessage] = {
val entityTypes = Seq(entity.entityType.getOrElse(throw ValidationException("need an entity type")))
agoraBusiness.findSingle(entity, entityTypes, AccessControl.publicUser) map { foundEntity =>
descriptorType match {
case ToolDescriptorType.WDL =>
// the url we return here is known to be incorrect in FireCloud (GAWB-1741).
// we return it anyway because it still provides some information, even if it
// requires manual user intervention to work.
val result = ToolDescriptor(foundEntity.url.getOrElse(""),
foundEntity.payload.getOrElse(""),
ToolDescriptorType.WDL)
RequestComplete(result)
case ToolDescriptorType.PLAIN_WDL =>
RequestComplete(foundEntity.payload.getOrElse(""))
}
}
}

def queryPublic(requestContext: RequestContext,
agoraSearch: AgoraEntity): Future[PerRequestMessage] = {
agoraBusiness.find(agoraSearch, None, Seq(AgoraEntityType.Workflow), AccessControl.publicUser) map { entities =>
val toolVersions = entities map ToolVersion.apply
RequestComplete(toolVersions)
}
}

def queryPublicTool(requestContext: RequestContext,
agoraSearch: AgoraEntity): Future[PerRequestMessage] = {
agoraBusiness.find(agoraSearch, None, Seq(AgoraEntityType.Workflow), AccessControl.publicUser) map { entities =>
RequestComplete(Tool(entities))
}
}

def queryPublicTools(requestContext: RequestContext): Future[PerRequestMessage] = {
agoraBusiness.find(AgoraEntity(), None, Seq(AgoraEntityType.Workflow), AccessControl.publicUser) map { allentities =>
val groupedSnapshots = allentities.groupBy( ae => (ae.namespace,ae.name))
val tools:Seq[Tool] = (groupedSnapshots.values map { entities => Tool(entities )}).toSeq
RequestComplete(tools.sortBy(_.id))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,79 @@ package org.broadinstitute.dsde.agora.server.ga4gh
import akka.actor.Props
import com.typesafe.scalalogging.LazyLogging
import org.broadinstitute.dsde.agora.server.dataaccess.permissions.PermissionsDataSource
import org.broadinstitute.dsde.agora.server.model.{AgoraEntity, AgoraEntityType}
import org.broadinstitute.dsde.agora.server.ga4gh.Ga4ghServiceMessages._
import org.broadinstitute.dsde.agora.server.ga4gh.Models._
import org.broadinstitute.dsde.agora.server.model.AgoraEntityType
import org.broadinstitute.dsde.agora.server.webservice.PerRequestCreator
import org.broadinstitute.dsde.agora.server.webservice.handlers.QueryHandler
import org.broadinstitute.dsde.agora.server.webservice.util.ServiceMessages.QueryPublicSingle
import spray.http.{MediaTypes, StatusCodes}
import spray.httpx.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._
import spray.routing._

abstract class Ga4ghService(permissionsDataSource: PermissionsDataSource)
extends HttpService with Ga4ghServiceSupport with PerRequestCreator with LazyLogging {

override implicit val executionContext = scala.concurrent.ExecutionContext.Implicits.global

def queryHandler = Props(classOf[QueryHandler], permissionsDataSource, executionContext)
def queryHandler = Props(classOf[Ga4ghQueryHandler], permissionsDataSource, executionContext)

def routes =
pathPrefix("ga4gh") {
pathPrefix("tools") {
path (Segment / "versions" / Segment / Segment / "descriptor") { (id, versionId, descriptorType) =>
get { requestContext =>

val dType = parseDescriptorType(descriptorType)
val snapshotId = parseVersionId(versionId)
val toolId = parseId(id)

val entity = AgoraEntity(Some(toolId.namespace), Some(toolId.name), Some(snapshotId), entityType = Some(AgoraEntityType.Workflow))

val message = QueryPublicSingle(requestContext, entity, dType)
perRequest(requestContext, queryHandler, message)
pathPrefix("ga4gh" / "v1") {
get {
path("metadata") {
// because of the dashes in these names, using the automatic spray json marshalling is annoying.
// this is a constant response anyway, so hardcode it as a string.
val metadataResponse:String =
"""
|{
| "version": "1.0.0",
| "api-version": "1.0.0",
| "country": "USA",
| "friendly-name": "FireCloud"
|}
""".stripMargin
respondWithMediaType(MediaTypes.`application/json`) {
complete(StatusCodes.OK, metadataResponse)
}
} ~
path("tool-classes") {
val toolClassesResponse:Seq[ToolClass] = Seq(ToolClass.fromEntityType(Some(AgoraEntityType.Workflow)))
complete(toolClassesResponse)
} ~
path("tools") { requestContext =>
// TODO: query params and response headers
val message = QueryPublicTools(requestContext)
perRequest(requestContext, queryHandler, message)
} ~
path("tools" / Segment) { id => requestContext =>
val entity = entityFromArguments(id)
val message = QueryPublicTool(requestContext, entity)
perRequest(requestContext, queryHandler, message)
} ~
path("tools" / Segment / "versions") { id => requestContext =>
val entity = entityFromArguments(id)
val message = QueryPublic(requestContext, entity)
perRequest(requestContext, queryHandler, message)
} ~
path("tools" / Segment / "versions" / Segment) { (id, versionId) => requestContext =>
val entity = entityFromArguments(id, versionId)
val message = QueryPublicSingle(requestContext, entity)
perRequest(requestContext, queryHandler, message)
} ~
path("tools" / Segment / "versions" / Segment / "dockerfile") { (id, versionId) =>
complete(spray.http.StatusCodes.NotImplemented)
} ~
path("tools" / Segment / "versions" / Segment / Segment / "descriptor") { (id, versionId, descriptorType) => requestContext =>
val entity = entityFromArguments(id, versionId)
val message = QueryPublicSinglePayload(requestContext, entity, parseDescriptorType(descriptorType))
perRequest(requestContext, queryHandler, message)
} ~
path("tools" / Segment / "versions" / Segment / Segment / "descriptor" / Segment) { (id, versionId, descriptorType, relativePath) =>
complete(spray.http.StatusCodes.NotImplemented)
} ~
path("tools" / Segment / "versions" / Segment / Segment / "tests") { (id, versionId, descriptorType) =>
complete(spray.http.StatusCodes.NotImplemented)
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.broadinstitute.dsde.agora.server.ga4gh

import org.broadinstitute.dsde.agora.server.ga4gh.Models.ToolDescriptorType.DescriptorType
import org.broadinstitute.dsde.agora.server.model.AgoraEntity
import spray.routing.RequestContext

object Ga4ghServiceMessages {

case class QueryPublicSingle(requestContext: RequestContext,
entity: AgoraEntity)

case class QueryPublicSinglePayload(requestContext: RequestContext,
entity: AgoraEntity,
descriptorType: DescriptorType)

case class QueryPublic(requestContext: RequestContext,
agoraSearch: AgoraEntity)

case class QueryPublicTool(requestContext: RequestContext,
agoraSearch: AgoraEntity)

case class QueryPublicTools(requestContext: RequestContext)


}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package org.broadinstitute.dsde.agora.server.ga4gh
import org.broadinstitute.dsde.agora.server.exceptions.ValidationException
import org.broadinstitute.dsde.agora.server.ga4gh.Models.ToolDescriptorType.DescriptorType
import org.broadinstitute.dsde.agora.server.ga4gh.Models.{ToolDescriptorType, ToolId}
import org.broadinstitute.dsde.agora.server.model.{AgoraEntity, AgoraEntityType}

import scala.util.{Failure, Success, Try}

trait Ga4ghServiceSupport {

def parseId(id:String): ToolId = {
val ids = id.split(":")
val ids = id.split(Models.ID_DELIMITER)
if (ids.length != 2)
throw ValidationException(s"This service requires the id to be a colon-delimited namespace and name. Found: $id")

Expand All @@ -29,6 +30,22 @@ trait Ga4ghServiceSupport {
ToolDescriptorType.withName(descriptorType)
}

// validate url arguments supplied by the user and return an AgoraEntity that can be used as criteria
// for querying the db.
def entityFromArguments(id: String, versionId: String): AgoraEntity = {
argsToEntity(id, Some(versionId))
}

def entityFromArguments(id: String): AgoraEntity = {
argsToEntity(id, None)
}

private def argsToEntity(id: String, versionId: Option[String]): AgoraEntity = {
val snapshotOption:Option[Int] = versionId map parseVersionId
val toolId = parseId(id)

AgoraEntity(Some(toolId.namespace), Some(toolId.name), snapshotOption, entityType = Some(AgoraEntityType.Workflow))
}


}
Loading

0 comments on commit ce8429f

Please sign in to comment.