diff --git a/commons/protocol/src/main/scala/net/shrine/protocol/FlagQueryRequest.scala b/commons/protocol/src/main/scala/net/shrine/protocol/FlagQueryRequest.scala index fcb1a8bf9..607073141 100644 --- a/commons/protocol/src/main/scala/net/shrine/protocol/FlagQueryRequest.scala +++ b/commons/protocol/src/main/scala/net/shrine/protocol/FlagQueryRequest.scala @@ -1,98 +1,104 @@ package net.shrine.protocol +import net.shrine.log.Loggable + import scala.concurrent.duration.Duration import scala.util.Try import scala.xml.NodeSeq import net.shrine.serialization.I2b2Unmarshaller import net.shrine.util.XmlUtil import net.shrine.util.NodeSeqEnrichments import net.shrine.util.XmlUtil import scala.xml.NodeBuffer import net.shrine.serialization.I2b2Unmarshaller import net.shrine.serialization.I2b2UnmarshallingHelpers /** * @author Clint Gilbert * @date March, 2014 * * NB: Now needs to be serializable as an i2b2 blob, because even though flagging is a Shrine-only feature, * flagging will be initiated from the legacy i2b2 webclient for the forseeable future. In that context, * generating i2b2 XML blobs and running them through the proxy is the path of least resistance. Sigh. * - 17 June 2014 */ final case class FlagQueryRequest( override val projectId: String, override val waitTime: Duration, override val authn: AuthenticationInfo, networkQueryId: Long, message: Option[String]) extends ShrineRequest(projectId, waitTime, authn) with HandleableI2b2Request with HasHeaderFields { override val requestType = RequestType.FlagQueryRequest override def handleI2b2(handler: I2b2RequestHandler, shouldBroadcast: Boolean): ShrineResponse = { handler.flagQuery(this, shouldBroadcast) } import FlagQueryRequest.rootTagName override def toXml = XmlUtil.stripWhitespace { XmlUtil.renameRootTag(rootTagName) { { headerFragment } { networkQueryId } { message.map(m => { m }).orNull } } } protected override def i2b2MessageBody = XmlUtil.stripWhitespace { { headerFragment } { XmlUtil.renameRootTag(rootTagName) { { networkQueryId } { message.map(m => { m }).orNull } } } } } -object FlagQueryRequest extends I2b2XmlUnmarshaller[FlagQueryRequest] with ShrineXmlUnmarshaller[FlagQueryRequest] with ShrineRequestUnmarshaller with I2b2UnmarshallingHelpers with HasRootTagName { +object FlagQueryRequest extends I2b2XmlUnmarshaller[FlagQueryRequest] with ShrineXmlUnmarshaller[FlagQueryRequest] with ShrineRequestUnmarshaller with I2b2UnmarshallingHelpers with HasRootTagName with Loggable { override val rootTagName = "flagQuery" override def fromXml(breakdownTypes: Set[ResultOutputType])(xml: NodeSeq): Try[FlagQueryRequest] = { import NodeSeqEnrichments.Strictness._ for { tagName <- Try(xml.head.label) if tagName == rootTagName projectId <- shrineProjectId(xml) waitTimeMs <- shrineWaitTime(xml) authn <- shrineAuthenticationInfo(xml) networkQueryId <- xml.withChild("networkQueryId").map(_.text.toLong) message = (xml \ "message").headOption.map(_.text) } yield { + info(s"FlagQueryRequest fromXML $message from $xml") + FlagQueryRequest(projectId, waitTimeMs, authn, networkQueryId, message) } } override def fromI2b2(breakdownTypes: Set[ResultOutputType])(xml: NodeSeq): Try[FlagQueryRequest] = { import NodeSeqEnrichments.Strictness._ for { projectId <- i2b2ProjectId(xml) waitTime <- i2b2WaitTime(xml) authn <- i2b2AuthenticationInfo(xml) networkQueryId <- (xml withChild "message_body" withChild "request" withChild rootTagName withChild "networkQueryId").map(_.text.toLong) message = (xml \ "message_body" \ "request" \ rootTagName \ "message").headOption.map(_.text) } yield { + info(s"FlagQueryRequest fromI2b2 $message from $xml") + FlagQueryRequest(projectId, waitTime, authn, networkQueryId, message) } } } \ No newline at end of file diff --git a/qep/service/src/main/scala/net/shrine/qep/ShrineResource.scala b/qep/service/src/main/scala/net/shrine/qep/ShrineResource.scala index 089c7eec7..09a3636f4 100644 --- a/qep/service/src/main/scala/net/shrine/qep/ShrineResource.scala +++ b/qep/service/src/main/scala/net/shrine/qep/ShrineResource.scala @@ -1,259 +1,259 @@ package net.shrine.qep import javax.ws.rs.Consumes import javax.ws.rs.DELETE import javax.ws.rs.GET import javax.ws.rs.HeaderParam import javax.ws.rs.POST import javax.ws.rs.Path import javax.ws.rs.PathParam import javax.ws.rs.Produces import javax.ws.rs.QueryParam import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response import net.shrine.log.Loggable import net.shrine.protocol.AuthenticationInfo import net.shrine.protocol.DeleteQueryRequest import net.shrine.protocol.OutputTypeSet import net.shrine.protocol.ReadApprovedQueryTopicsRequest import net.shrine.protocol.ReadInstanceResultsRequest import net.shrine.protocol.ReadPdoRequest import net.shrine.protocol.ReadPreviousQueriesRequest import net.shrine.protocol.ReadQueryDefinitionRequest import net.shrine.protocol.ReadQueryInstancesRequest import net.shrine.protocol.ReadQueryResultRequest import net.shrine.protocol.RenameQueryRequest import net.shrine.protocol.RunQueryRequest import net.shrine.protocol.ShrineRequestHandler import net.shrine.protocol.query.QueryDefinition import scala.xml.XML import net.shrine.protocol.BaseShrineResponse import net.shrine.protocol.ReadTranslatedQueryDefinitionRequest import net.shrine.protocol.FlagQueryRequest import net.shrine.protocol.UnFlagQueryRequest /** * @author Bill Simons * @author Clint Gilbert * @since 8/30/11 * @see http://cbmi.med.harvard.edu * @see http://chip.org *

* NOTICE: This software comes with NO guarantees whatsoever and is * licensed as Lgpl Open Source * @see http://www.gnu.org/licenses/lgpl.html */ @Path("/shrine") @Produces(Array(MediaType.APPLICATION_XML)) //NB: Is a case class to get apply on the companion object, for smoother testing final case class ShrineResource(shrineRequestHandler: ShrineRequestHandler) extends Loggable { import ShrineResource.waitTime @Consumes(Array(MediaType.TEXT_PLAIN)) @POST @Path("/queries/{queryId}/flag") def flagQuery( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") networkQueryId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean, flagMessage: String): String = { info(s"flagMessage string is $flagMessage") - val flagMessageOption = Option(flagMessage).filter(!_.trim.isEmpty) + val flagMessageOption: Option[String] = Option(flagMessage).filter(!_.trim.isEmpty) //TODO: What should we return, if anything? performAndSerialize(_.flagQuery(FlagQueryRequest(projectId, waitTime, authorization, networkQueryId, flagMessageOption), shouldBroadcast)) } @POST @Path("/queries/{queryId}/unflag") def unFlagQuery( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") networkQueryId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { //TODO: What should we return, if anything? performAndSerialize(_.unFlagQuery(UnFlagQueryRequest(projectId, waitTime, authorization, networkQueryId), shouldBroadcast)) } @GET @Path("{userId}/approved-topics") def readApprovedQueryTopics( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("userId") userId: String, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.readApprovedQueryTopics(ReadApprovedQueryTopicsRequest(projectId, waitTime, authorization, userId), shouldBroadcast)) } @GET @Path("{userId}/queries") def readPreviousQueries( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("userId") userId: String, @QueryParam("fetchSize") fetchSize: Int, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): Response = { if (userId != authorization.username) { Response.status(403).build } else { val fSize = if (fetchSize != 0) fetchSize else 20 Response.ok.entity { performAndSerialize(_.readPreviousQueries(ReadPreviousQueriesRequest(projectId, waitTime, authorization, userId, fSize), shouldBroadcast)) }.build } } @POST @Path("/queries") @Consumes(Array(MediaType.APPLICATION_XML)) def runQuery( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @HeaderParam("topicId") topicId: String, @HeaderParam("topicName") topicName: String, //outputTypes will be constructed by JAXRS using the String value of the 'outputTypes' header @HeaderParam("outputTypes") outputTypes: OutputTypeSet, queryDefinitionXml: String, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { val queryDef = QueryDefinition.fromXml(queryDefinitionXml).get val topicIdOption = Option(topicId).filter(!_.trim.isEmpty) val topicNameOption = Option(topicName).filter(!_.trim.isEmpty) debug(s"runQuery() with $shrineRequestHandler and $queryDef") performAndSerialize(_.runQuery(RunQueryRequest(projectId, waitTime, authorization, topicIdOption, topicNameOption, outputTypes.toSet, queryDef), shouldBroadcast)) } @GET @Path("/queries/{queryId}/instances") def readQueryInstances( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") queryId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.readQueryInstances(ReadQueryInstancesRequest(projectId, waitTime, authorization, queryId), shouldBroadcast)) } @GET @Path("/instances/{instanceId}/results") def readInstanceResults( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("instanceId") instanceId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.readInstanceResults(ReadInstanceResultsRequest(projectId, waitTime, authorization, instanceId), shouldBroadcast)) } @POST //This must be POST, since we're sending content in the request body @Path("/patient-set/{patientSetCollId}") @Consumes(Array(MediaType.APPLICATION_XML)) def readPdo( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("patientSetCollId") patientSetCollId: String, optionsXml: String, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { import XML.loadString performAndSerialize(_.readPdo(ReadPdoRequest(projectId, waitTime, authorization, patientSetCollId, loadString(optionsXml)), shouldBroadcast)) } @GET @Path("/queries/{queryId}") def readQueryDefinition( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") queryId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.readQueryDefinition(ReadQueryDefinitionRequest(projectId, waitTime, authorization, queryId), shouldBroadcast)) } @DELETE @Path("/queries/{queryId}") def deleteQuery( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") queryId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.deleteQuery(DeleteQueryRequest(projectId, waitTime, authorization, queryId), shouldBroadcast)) } @POST @Path("/queries/{queryId}/name") @Consumes(Array(MediaType.TEXT_PLAIN)) def renameQuery( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") queryId: Long, queryName: String, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.renameQuery(RenameQueryRequest(projectId, waitTime, authorization, queryId, queryName), shouldBroadcast)) } @GET @Path("/queries/{queryId}/results") @Consumes(Array(MediaType.TEXT_PLAIN)) def readQueryResults( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @PathParam("queryId") queryId: Long, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean): String = { performAndSerialize(_.readQueryResult(ReadQueryResultRequest(projectId, waitTime, authorization, queryId), shouldBroadcast)) } @POST @Path("/queries/translated") @Consumes(Array(MediaType.APPLICATION_XML)) def readTranslatedQueryDefinition( @HeaderParam("projectId") projectId: String, //authorization will be constructed by JAXRS using the String value of the 'Authorization' header @HeaderParam("Authorization") authorization: AuthenticationInfo, @HeaderParam("shouldBroadcast") shouldBroadcast: Boolean, queryDefinitionXml: String): String = { val queryDef = QueryDefinition.fromXml(queryDefinitionXml).get //NB: Create the RunQueryRequest with a dummy networkQueryId of '-1'; //this will be filled in with an appropriately-generated value by the ShrineRequestHandler performAndSerialize(_.readTranslatedQueryDefinition(ReadTranslatedQueryDefinitionRequest(authorization, waitTime, queryDef), shouldBroadcast)) } private def performAndSerialize[R <: BaseShrineResponse](op: ShrineRequestHandler => R): String = { op(shrineRequestHandler).toXmlString } } //NB: extends ShrineRequestHandler => ShrineResource for smoother testing syntax object ShrineResource extends (ShrineRequestHandler => ShrineResource) { import scala.concurrent.duration._ val waitTime = 10.seconds }