diff --git a/commons/protocol/src/main/scala/net/shrine/protocol/DeleteQueryRequest.scala b/commons/protocol/src/main/scala/net/shrine/protocol/DeleteQueryRequest.scala
index f5d383425..29cc60bea 100644
--- a/commons/protocol/src/main/scala/net/shrine/protocol/DeleteQueryRequest.scala
+++ b/commons/protocol/src/main/scala/net/shrine/protocol/DeleteQueryRequest.scala
@@ -1,85 +1,89 @@
package net.shrine.protocol
import scala.concurrent.duration.Duration
-import scala.util.Try
+import scala.util.{Failure, Success, Try}
import scala.xml.NodeSeq
import net.shrine.util.XmlUtil
import net.shrine.util.NodeSeqEnrichments
import net.shrine.serialization.I2b2UnmarshallingHelpers
/**
* @author Bill Simons
* @since 3/28/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
*
* NB: this is a case class to get a structural equality contract in hashCode and equals, mostly for testing
*/
final case class DeleteQueryRequest(
override val projectId: String,
override val waitTime: Duration,
override val authn: AuthenticationInfo,
networkQueryId: Long) extends ShrineRequest(projectId, waitTime, authn) with CrcRequest with TranslatableRequest[DeleteQueryRequest] with HandleableShrineRequest with HandleableI2b2Request {
override val requestType = RequestType.MasterDeleteRequest
override def handle(handler: ShrineRequestHandler, shouldBroadcast: Boolean) = handler.deleteQuery(this, shouldBroadcast)
override def handleI2b2(handler: I2b2RequestHandler, shouldBroadcast: Boolean) = handler.deleteQuery(this, shouldBroadcast)
override def toXml: NodeSeq = XmlUtil.stripWhitespace {
{ headerFragment }
{ networkQueryId }
}
def withId(id: Long) = this.copy(networkQueryId = id)
override def withAuthn(ai: AuthenticationInfo) = this.copy(authn = ai)
override def withProject(proj: String) = this.copy(projectId = proj)
protected override def i2b2MessageBody = XmlUtil.stripWhitespace {
{ i2b2PsmHeader }
{ authn.username }
{ networkQueryId }
}
}
object DeleteQueryRequest extends I2b2XmlUnmarshaller[DeleteQueryRequest] with ShrineXmlUnmarshaller[DeleteQueryRequest] with ShrineRequestUnmarshaller with I2b2UnmarshallingHelpers {
override def fromI2b2(breakdownTypes: Set[ResultOutputType])(xml: NodeSeq): Try[DeleteQueryRequest] = {
import NodeSeqEnrichments.Strictness._
for {
projectId <- i2b2ProjectId(xml)
waitTime <- i2b2WaitTime(xml)
authn <- i2b2AuthenticationInfo(xml)
- masterId <- (xml withChild "message_body" withChild "request" withChild "query_master_id").map(_.text.toLong)
+ masterIdString <- (xml withChild "message_body" withChild "request" withChild "query_master_id").map(_.text)
+ masterId <- Try(masterIdString.toLong).transform(id => Success(id),
+ x => Failure(I2B2MessageFormatException(s"query_master_id of $masterIdString could not be interpreted as a Long integer",x)))
} yield {
DeleteQueryRequest(projectId, waitTime, authn, masterId)
}
}
override def fromXml(breakdownTypes: Set[ResultOutputType])(xml: NodeSeq): Try[DeleteQueryRequest] = {
import NodeSeqEnrichments.Strictness._
for {
waitTime <- shrineWaitTime(xml)
authn <- shrineAuthenticationInfo(xml)
queryId <- xml.withChild("queryId").map(_.text.toLong)
projectId <- shrineProjectId(xml)
} yield {
DeleteQueryRequest(projectId, waitTime, authn, queryId)
}
}
-}
\ No newline at end of file
+}
+
+case class I2B2MessageFormatException(message:String,cause:Throwable) extends Exception(message,cause)
\ No newline at end of file
diff --git a/qep/service/src/main/scala/net/shrine/qep/I2b2BroadcastResource.scala b/qep/service/src/main/scala/net/shrine/qep/I2b2BroadcastResource.scala
index ad4e4a949..73c2d7d68 100644
--- a/qep/service/src/main/scala/net/shrine/qep/I2b2BroadcastResource.scala
+++ b/qep/service/src/main/scala/net/shrine/qep/I2b2BroadcastResource.scala
@@ -1,97 +1,106 @@
package net.shrine.qep
import java.sql.SQLException
import javax.ws.rs.{POST, Path, Produces}
import javax.ws.rs.core.{MediaType, Response}
import javax.ws.rs.core.Response.ResponseBuilder
import net.shrine.authentication.NotAuthenticatedException
import net.shrine.log.Loggable
-import net.shrine.problem.ProblemNotYetEncoded
-import net.shrine.protocol.{ErrorResponse, HandleableI2b2Request, I2b2RequestHandler, ResultOutputType, ShrineRequest}
+import net.shrine.problem.{AbstractProblem, ProblemNotYetEncoded, ProblemSources}
+import net.shrine.protocol.{ErrorResponse, HandleableI2b2Request, I2B2MessageFormatException, I2b2RequestHandler, ResultOutputType, ShrineRequest}
import net.shrine.qep.queries.QepDatabaseProblem
import net.shrine.serialization.I2b2Marshaller
import net.shrine.slick.CouldNotRunDbIoActionException
import net.shrine.util.XmlUtil
import scala.util.Try
import scala.util.control.NonFatal
import scala.xml.NodeSeq
/**
* @author Bill Simons
* @since 3/10/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("/i2b2")
@Produces(Array(MediaType.APPLICATION_XML))
final case class I2b2BroadcastResource(i2b2RequestHandler: I2b2RequestHandler, breakdownTypes: Set[ResultOutputType]) extends Loggable {
//NB: Always broadcast when receiving requests from the legacy i2b2/Shrine webclient, since we can't retrofit it to
//Say whether broadcasting is desired for a praticular query/operation
val shouldBroadcast = true
@POST
@Path("request")
def doRequest(i2b2Request: String): Response = processI2b2Message(i2b2Request)
@POST
@Path("pdorequest")
def doPDORequest(i2b2Request: String): Response = processI2b2Message(i2b2Request)
def processI2b2Message(i2b2Request: String): Response = {
// todo would be good to log $i2b2Request)")
def errorResponse(e: Throwable): ErrorResponse = e match {
case nax:NotAuthenticatedException => ErrorResponse(nax.problem)
case cnrdax:CouldNotRunDbIoActionException => ErrorResponse(QepDatabaseProblem(cnrdax))
case sqlx:SQLException => ErrorResponse(QepDatabaseProblem(sqlx))
+ case imfx:I2B2MessageFormatException => ErrorResponse(QepCouldNotInterpretRequest(imfx))
case _ => ErrorResponse(ProblemNotYetEncoded("The QEP encountered an unforeseen problem while processing an i2b2 request",e))
}
def prettyPrint(xml: NodeSeq): String = XmlUtil.prettyPrint(xml.head).trim
//NB: The legacy webclient can't deal with non-200 status codes.
//It also can't deal with ErrorResponses in several cases, but we have nothing better to return for now.
//TODO: Return a 500 status here, once we're using the new web client
def i2b2HttpErrorResponse(e: Throwable): ResponseBuilder = Response.ok.entity(prettyPrint(errorResponse(e).toI2b2))
def handleRequest(shrineRequest: ShrineRequest with HandleableI2b2Request): Try[ResponseBuilder] = Try {
info(s"Running request from user: ${shrineRequest.authn.username} of type ${shrineRequest.requestType.toString}")
val shrineResponse = shrineRequest.handleI2b2(i2b2RequestHandler, shouldBroadcast)
//TODO: Revisit this. For now, we bail if we get something that isn't i2b2able
val responseString: String = shrineResponse match {
case i2b2able: I2b2Marshaller => prettyPrint(i2b2able.toI2b2)
case _ => throw new Exception(s"Shrine response $shrineResponse has no i2b2 representation")
}
Response.ok.entity(responseString)
}.recover {
case NonFatal(e) =>
error("Error processing request: ", e)
i2b2HttpErrorResponse(e)
}
def handleParseError(e: Throwable): Try[ResponseBuilder] = Try {
debug(s"Failed to unmarshal i2b2 request.")//todo would be good to log : $i2b2Request")
error("Couldn't understand request: ", e)
//NB: The legacy webclient can't deal with non-200 status codes.
//It also can't deal with ErrorResponses in several cases, but we have nothing better to return for now.
//TODO: Return a 400 status here, once we're using the new web client
i2b2HttpErrorResponse(e)
}
val builder = HandleableI2b2Request.fromI2b2String(breakdownTypes)(i2b2Request).transform(handleRequest, handleParseError).get
builder.build()
}
+}
+
+case class QepCouldNotInterpretRequest(x:Exception) extends AbstractProblem(ProblemSources.Qep){
+ override val summary = "The QEP could not interpret a request."
+
+ override val throwable = Some(x)
+
+ override val description = x.getMessage
}
\ No newline at end of file