diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/model/ShrineQuery.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/model/ShrineQuery.scala index 2d167dd0c..a851498b4 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/model/ShrineQuery.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/model/ShrineQuery.scala @@ -1,37 +1,35 @@ package net.shrine.adapter.dao.model import javax.xml.datatype.XMLGregorianCalendar import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.QueryMaster /** * @author clint * @since Oct 16, 2012 * * NB: Can't be final, since Squeryl runs this class through cglib to make a synthetic subclass :( */ final case class ShrineQuery( id: Int, localId: String, networkId: Long, name: String, username: String, domain: String, dateCreated: XMLGregorianCalendar, isFlagged: Boolean, - hasBeenRun: Boolean, + hasBeenRun: Boolean, //todo this goes next flagMessage: Option[String], queryDefinition: QueryDefinition) { def hasNotBeenRun: Boolean = !hasBeenRun def withName(newName: String): ShrineQuery = this.copy(name = newName) //NB: Due to the new i2b2 admin previous queries API, we need to be able to transform //ourselves into a QueryMaster using either the network or local id . def toQueryMaster(idField: ShrineQuery => String = _.networkId.toString): QueryMaster = { - val held = hasNotBeenRun - - QueryMaster(idField(this), networkId, name, username, domain, dateCreated, Some(held), Some(isFlagged), if(isFlagged) flagMessage else None) + QueryMaster(idField(this), networkId, name, username, domain, dateCreated, Some(isFlagged), if(isFlagged) flagMessage else None) } } diff --git a/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTestHelpers.scala b/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTestHelpers.scala index 96087d8f9..917b3056e 100644 --- a/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTestHelpers.scala +++ b/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTestHelpers.scala @@ -1,54 +1,54 @@ package net.shrine.adapter import net.shrine.protocol.AuthenticationInfo import net.shrine.protocol.Credential import net.shrine.protocol.QueryMaster import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.query.Term import net.shrine.util.XmlDateHelper /** * @author clint - * @date Nov 28, 2012 + * @since Nov 28, 2012 */ trait AdapterTestHelpers { val queryId = 123 val localMasterId = "kasjdlsajdklajsdkljasd" val bogusQueryId = 999 val masterId1 = "1" val masterId2 = "2" val masterId3 = "3" val masterId4 = "4" val networkQueryId1 = 1L val networkQueryId2 = 2L val networkQueryId3 = 3L val networkQueryId4 = 4L val queryName1 = "query-name1" val queryName2 = "query-name2" lazy val queryDef1 = QueryDefinition(queryName1, Term("x")) lazy val queryDef2 = QueryDefinition(queryName2, Term("y")) val userId = "some-other-user" val domain = "Some-other-domain" val anotherDomain = "some-completely-different-domain" val password = "some-val" - lazy val authn = AuthenticationInfo(domain, userId, Credential(password, false)) - lazy val authn2 = AuthenticationInfo(authn.domain, "a-different-user", Credential("jkafhkjdhsfjksdhfkjsdg", false)) + lazy val authn = AuthenticationInfo(domain, userId, Credential(password, isToken = false)) + lazy val authn2 = AuthenticationInfo(authn.domain, "a-different-user", Credential("jkafhkjdhsfjksdhfkjsdg", isToken = false)) val projectId = "some-project-id" import scala.concurrent.duration._ val waitTime = 12345.milliseconds - lazy val queryMaster1 = QueryMaster(masterId1, networkQueryId1, queryName1, userId, domain, XmlDateHelper.now, held = Some(false), flagged = Some(true)) - lazy val queryMaster2 = QueryMaster(masterId2, networkQueryId2, queryName2, userId, domain, XmlDateHelper.now, held = Some(false), flagged = Some(false)) - lazy val queryMaster4 = QueryMaster(masterId4, networkQueryId4, queryName2, authn2.username, anotherDomain, XmlDateHelper.now, held = Some(false), flagged = Some(true)) + lazy val queryMaster1 = QueryMaster(masterId1, networkQueryId1, queryName1, userId, domain, XmlDateHelper.now, flagged = Some(true)) + lazy val queryMaster2 = QueryMaster(masterId2, networkQueryId2, queryName2, userId, domain, XmlDateHelper.now, flagged = Some(false)) + lazy val queryMaster4 = QueryMaster(masterId4, networkQueryId4, queryName2, authn2.username, anotherDomain, XmlDateHelper.now, flagged = Some(true)) } \ No newline at end of file diff --git a/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/AbstractI2b2AdminResourceJaxrsTest.scala b/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/AbstractI2b2AdminResourceJaxrsTest.scala index 45636c915..8d65f28d7 100644 --- a/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/AbstractI2b2AdminResourceJaxrsTest.scala +++ b/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/AbstractI2b2AdminResourceJaxrsTest.scala @@ -1,117 +1,101 @@ package net.shrine.adapter.service -import scala.xml.XML -import org.junit.After -import org.junit.Before -import net.shrine.util.ShouldMatchersForJUnit +import junit.framework.TestCase import net.shrine.adapter.AdapterTestHelpers import net.shrine.adapter.dao.squeryl.AbstractSquerylAdapterTest -import net.shrine.client.HttpClient -import net.shrine.client.HttpResponse -import net.shrine.client.JerseyHttpClient -import net.shrine.protocol.ErrorResponse -import net.shrine.protocol.QueryMaster -import net.shrine.protocol.ReadI2b2AdminPreviousQueriesRequest -import net.shrine.protocol.ReadPreviousQueriesResponse -import net.shrine.protocol.ReadQueryDefinitionRequest -import net.shrine.protocol.ReadQueryDefinitionResponse -import net.shrine.protocol.query.QueryDefinition -import net.shrine.util.XmlUtil +import net.shrine.client.{HttpClient, HttpResponse, JerseyHttpClient} import net.shrine.crypto.TrustParam.AcceptAllCerts -import net.shrine.protocol.I2b2AdminRequestHandler -import net.shrine.protocol.I2b2AdminReadQueryDefinitionRequest -import junit.framework.TestCase -import net.shrine.protocol.DefaultBreakdownResultOutputTypes +import net.shrine.protocol.{DefaultBreakdownResultOutputTypes, ErrorResponse, I2b2AdminReadQueryDefinitionRequest, I2b2AdminRequestHandler, QueryMaster, ReadI2b2AdminPreviousQueriesRequest, ReadPreviousQueriesResponse, ReadQueryDefinitionResponse} +import net.shrine.protocol.query.QueryDefinition +import net.shrine.util.{ShouldMatchersForJUnit, XmlUtil} +import org.junit.{After, Before} + +import scala.xml.XML /** * @author clint - * @date Apr 24, 2013 + * @since Apr 24, 2013 */ abstract class AbstractI2b2AdminResourceJaxrsTest extends TestCase with JerseyTestComponent[I2b2AdminRequestHandler] with AbstractSquerylAdapterTest with ShouldMatchersForJUnit with CanLoadTestData with AdapterTestHelpers { import scala.concurrent.duration._ protected def adminClient = I2b2AdminClient(resourceUrl, JerseyHttpClient(AcceptAllCerts, 5.minutes)) override def resourceClass(handler: I2b2AdminRequestHandler) = I2b2AdminResource(handler, DefaultBreakdownResultOutputTypes.toSet) override val basePath = "i2b2/admin/request" @Before override def setUp(): Unit = this.JerseyTest.setUp() @After override def tearDown(): Unit = this.JerseyTest.tearDown() protected object NeverAuthenticatesMockPmHttpClient extends HttpClient { override def post(input: String, url: String): HttpResponse = HttpResponse.ok(ErrorResponse("blarg").toI2b2String) } protected object AlwaysAuthenticatesMockPmHttpClient extends HttpClient { override def post(input: String, url: String): HttpResponse = { HttpResponse.ok(XmlUtil.stripWhitespace { Some user { userId } { domain } { password } MANAGER }.toString) } } protected def doTestReadQueryDefinition(networkQueryId: Long, expectedQueryNameAndQueryDef: Option[(String, QueryDefinition)]) { val request = I2b2AdminReadQueryDefinitionRequest(projectId, waitTime, authn, networkQueryId) - val currentHandler = handler - val resp = adminClient.readQueryDefinition(request) def stripNamespaces(s: String) = XmlUtil.stripNamespaces(XML.loadString(s)) expectedQueryNameAndQueryDef match { case Some((expectedQueryName, expectedQueryDef)) => { val response @ ReadQueryDefinitionResponse(masterId, name, userId, createDate, queryDefinition) = resp masterId should be(networkQueryId) name should be(expectedQueryName) userId should be(authn.username) createDate should not be (null) //NB: I'm not sure why whacky namespaces were coming back from the resource; //this checks that the gist of the queryDef XML makes it back. //TODO: revisit this stripNamespaces(queryDefinition) should equal(stripNamespaces(expectedQueryDef.toI2b2String)) } case None => resp.isInstanceOf[ErrorResponse] should be(true) } } protected def doTestReadI2b2AdminPreviousQueries(request: ReadI2b2AdminPreviousQueriesRequest, expectedQueryMasters: Seq[QueryMaster]) { - val currentHandler = handler - val ReadPreviousQueriesResponse(queryMasters) = adminClient.readI2b2AdminPreviousQueries(request) if(expectedQueryMasters.isEmpty) { queryMasters.isEmpty should be(true) } else { (queryMasters zip expectedQueryMasters).foreach { case (queryMaster, expected) => queryMaster.createDate should not be(null) queryMaster.name should equal(expected.name) queryMaster.queryMasterId should equal(expected.queryMasterId) queryMaster.userId should equal(expected.userId) queryMaster.groupId should equal(expected.groupId) queryMaster.flagged should equal(expected.flagged) - queryMaster.held should equal(expected.held) } } } } \ No newline at end of file diff --git a/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/I2b2AdminResourceJaxrsTest.scala b/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/I2b2AdminResourceJaxrsTest.scala index 7e35931e4..c58f32baa 100644 --- a/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/I2b2AdminResourceJaxrsTest.scala +++ b/adapter/adapter-service/src/test/scala/net/shrine/adapter/service/I2b2AdminResourceJaxrsTest.scala @@ -1,213 +1,194 @@ package net.shrine.adapter.service +import com.sun.jersey.test.framework.{AppDescriptor, JerseyTest} +import net.shrine.adapter.service.I2b2AdminResourceJaxrsTest._ +import net.shrine.client.JerseyHttpClient +import net.shrine.crypto.TrustParam.AcceptAllCerts import net.shrine.problem.TestProblem +import net.shrine.protocol.{AbstractReadQueryDefinitionRequest, AuthenticationInfo, Credential, DefaultBreakdownResultOutputTypes, I2b2AdminReadQueryDefinitionRequest, I2b2AdminRequestHandler, I2b2AdminUserWithRole, QueryMaster, QueryResult, ReadI2b2AdminPreviousQueriesRequest, ReadI2b2AdminQueryingUsersRequest, ReadI2b2AdminQueryingUsersResponse, ReadPreviousQueriesResponse, ReadQueryDefinitionResponse, RunHeldQueryRequest, RunQueryResponse, ShrineResponse} +import net.shrine.protocol.query.{QueryDefinition, Term} +import net.shrine.util.{JerseyAppDescriptor, ShouldMatchersForJUnit, XmlDateHelper, XmlUtil} +import org.junit.{After, Before, Test} import scala.xml.XML -import org.junit.Test -import net.shrine.util.ShouldMatchersForJUnit -import com.sun.jersey.test.framework.AppDescriptor -import com.sun.jersey.test.framework.JerseyTest -import I2b2AdminResourceJaxrsTest._ -import net.shrine.client.JerseyHttpClient -import net.shrine.protocol.AuthenticationInfo -import net.shrine.protocol.Credential -import net.shrine.protocol.I2b2AdminRequestHandler -import net.shrine.protocol.QueryMaster -import net.shrine.protocol.ReadI2b2AdminPreviousQueriesRequest -import net.shrine.protocol.ReadI2b2AdminPreviousQueriesRequest -import net.shrine.protocol.ReadPreviousQueriesResponse -import net.shrine.protocol.ReadQueryDefinitionRequest -import net.shrine.protocol.ReadQueryDefinitionResponse -import net.shrine.protocol.ShrineResponse -import net.shrine.protocol.query.QueryDefinition -import net.shrine.protocol.query.Term -import net.shrine.util.JerseyAppDescriptor -import net.shrine.util.XmlDateHelper -import net.shrine.util.XmlUtil -import net.shrine.crypto.TrustParam.AcceptAllCerts -import net.shrine.protocol.ReadI2b2AdminQueryingUsersRequest -import net.shrine.protocol.I2b2AdminUserWithRole -import net.shrine.protocol.ReadI2b2AdminQueryingUsersResponse -import net.shrine.protocol.I2b2AdminReadQueryDefinitionRequest -import net.shrine.protocol.AbstractReadQueryDefinitionRequest -import net.shrine.protocol.RunHeldQueryRequest -import net.shrine.protocol.RunQueryResponse -import net.shrine.protocol.QueryResult -import junit.framework.TestCase -import org.junit.Before -import org.junit.After -import net.shrine.protocol.DefaultBreakdownResultOutputTypes -import net.shrine.protocol.ShrineRequestHandler /** * @author clint - * @date Apr 10, 2013 + * @since Apr 10, 2013 */ +//noinspection ScalaUnnecessaryParentheses final class I2b2AdminResourceJaxrsTest extends JerseyTest with ShouldMatchersForJUnit { var handler: MockShrineRequestHandler = _ def resourceUrl = resource.getURI.toString + "i2b2/admin/request" override def configure: AppDescriptor = { JerseyAppDescriptor.thatCreates((h: I2b2AdminRequestHandler) => I2b2AdminResource(h, DefaultBreakdownResultOutputTypes.toSet)).using { handler = new MockShrineRequestHandler handler } } @Before override def setUp(): Unit = super.setUp() @After override def tearDown(): Unit = super.tearDown() import scala.concurrent.duration._ def adminClient = I2b2AdminClient(resourceUrl, new JerseyHttpClient(AcceptAllCerts, 5.minutes)) @Test - def testReadQueryDefinition { + def testReadQueryDefinition() { val queryId = 987654321L val request = I2b2AdminReadQueryDefinitionRequest(projectId, waitTime, authn, queryId) val currentHandler = handler val response = adminClient.readQueryDefinition(request).asInstanceOf[ReadQueryDefinitionResponse] response should not be(null) response.masterId should equal(queryId) response.name should equal("some-query-name") response.createDate should not be(null) response.userId should equal(authn.username) def stripNamespaces(s: String) = XmlUtil.stripNamespaces(XML.loadString(s)) //NB: I'm not sure why whacky namespaces were coming back from the resource; //this checks that the gist of the queryDef XML makes it back. //TODO: revisit this stripNamespaces(response.queryDefinition) should equal(stripNamespaces(queryDef.toI2b2String)) currentHandler.shouldBroadcastParam should be(false) currentHandler.readI2b2AdminPreviousQueriesParam should be(null) currentHandler.readQueryDefinitionParam should equal(request) } @Test - def testReadI2b2AdminPreviousQueries { + def testReadI2b2AdminPreviousQueries() { val searchString = "asdk;laskd;lask;gdjsg" val maxResults = 123 val sortOrder = ReadI2b2AdminPreviousQueriesRequest.SortOrder.Ascending val categoryToSearchWithin = ReadI2b2AdminPreviousQueriesRequest.Category.All val searchStrategy = ReadI2b2AdminPreviousQueriesRequest.Strategy.Exact import ReadI2b2AdminPreviousQueriesRequest.Username._ val request = ReadI2b2AdminPreviousQueriesRequest(projectId, waitTime, authn, Exactly("@"), searchString, maxResults, None, sortOrder, searchStrategy, categoryToSearchWithin) val currentHandler = handler val response = adminClient.readI2b2AdminPreviousQueries(request).asInstanceOf[ReadPreviousQueriesResponse] response should not be(null) response.queryMasters should equal(Seq(queryMaster)) currentHandler.shouldBroadcastParam should be(false) currentHandler.readI2b2AdminPreviousQueriesParam should be(request) currentHandler.readQueryDefinitionParam should be(null) } @Test - def testReadI2b2QueryingUsers { + def testReadI2b2QueryingUsers() { val projectIdToSearchFor = "foo-project-id" val request = ReadI2b2AdminQueryingUsersRequest(projectId, waitTime, authn, projectIdToSearchFor) val currentHandler = handler val response = adminClient.readI2b2AdminQueryingUsers(request).asInstanceOf[ReadI2b2AdminQueryingUsersResponse] response should not be(null) response.users should equal(users) currentHandler.shouldBroadcastParam should be(false) currentHandler.readI2b2AdminPreviousQueriesParam should be(null) currentHandler.readI2b2AdminQueryingUsersParam should be(request) currentHandler.readQueryDefinitionParam should be(null) } } object I2b2AdminResourceJaxrsTest { private val queryDef = QueryDefinition("foo", Term("x")) private val userId = "some-user-id" private val domain = "some-domain" private lazy val authn = new AuthenticationInfo(domain, userId, new Credential("some-val", false)) private val projectId = "some-project-id" import scala.concurrent.duration._ private val waitTime = 12345.milliseconds - private lazy val queryMaster = QueryMaster("queryMasterId", 123456789L, "name", userId, domain, XmlDateHelper.now, Some(true), Some(true)) + private lazy val queryMaster = QueryMaster( + queryMasterId = "queryMasterId", + networkQueryId = 123456789L, + name = "name", + userId = userId, + groupId = domain, + createDate = XmlDateHelper.now, + flagged = Some(true)) private lazy val users = Seq( I2b2AdminUserWithRole("projectId1", "joe user", "some important role"), I2b2AdminUserWithRole("projectId2", "jane user", "some other important role"), I2b2AdminUserWithRole("projectId3", "some user", "some super important role")) /** * Mock ShrineRequestHandler; stores passed parameters for later inspection. * Private, since this is (basically) the enclosing test class's state */ final class MockShrineRequestHandler extends I2b2AdminRequestHandler { private val lock = new AnyRef def shouldBroadcastParam = lock.synchronized(_shouldBroadcastParam) def readQueryDefinitionParam = lock.synchronized(_readQueryDefinitionParam) def readI2b2AdminPreviousQueriesParam = lock.synchronized(_readI2b2AdminPreviousQueriesParam) def readI2b2AdminQueryingUsersParam = lock.synchronized(_readI2b2AdminQueryingUsersParam) def runHeldQueryParam = lock.synchronized(_runHeldQueryParam) private var _shouldBroadcastParam = false private var _readQueryDefinitionParam: AbstractReadQueryDefinitionRequest = _ private var _readI2b2AdminPreviousQueriesParam: ReadI2b2AdminPreviousQueriesRequest = _ private var _readI2b2AdminQueryingUsersParam: ReadI2b2AdminQueryingUsersRequest = _ private var _runHeldQueryParam: RunHeldQueryRequest = _ override def readI2b2AdminPreviousQueries(request: ReadI2b2AdminPreviousQueriesRequest, shouldBroadcast: Boolean): ShrineResponse = setShouldBroadcastAndThen(shouldBroadcast) { lock.synchronized { _readI2b2AdminPreviousQueriesParam = request } ReadPreviousQueriesResponse(Seq(queryMaster)) } override def readI2b2AdminQueryingUsers(request: ReadI2b2AdminQueryingUsersRequest, shouldBroadcast: Boolean): ShrineResponse = setShouldBroadcastAndThen(shouldBroadcast) { lock.synchronized { _readI2b2AdminQueryingUsersParam = request } ReadI2b2AdminQueryingUsersResponse(users) } override def readQueryDefinition(request: I2b2AdminReadQueryDefinitionRequest, shouldBroadcast: Boolean): ShrineResponse = setShouldBroadcastAndThen(shouldBroadcast) { lock.synchronized { _readQueryDefinitionParam = request } ReadQueryDefinitionResponse(request.queryId, "some-query-name", request.authn.username, XmlDateHelper.now, queryDef.toI2b2String) } override def runHeldQuery(request: RunHeldQueryRequest, shouldBroadcast: Boolean): ShrineResponse = { lock.synchronized { _runHeldQueryParam = request } RunQueryResponse(request.networkQueryId, XmlDateHelper.now, request.authn.username, request.projectId, QueryDefinition("bogus", Term("placeholder")), 12345L, QueryResult.errorResult(Some("foo"), "not actually an error",TestProblem)) } private def setShouldBroadcastAndThen(shouldBroadcast: Boolean)(f: => ShrineResponse): ShrineResponse = { try { f } finally { lock.synchronized { _shouldBroadcastParam = shouldBroadcast } } } } } \ No newline at end of file diff --git a/commons/protocol/src/main/scala/net/shrine/protocol/QueryMaster.scala b/commons/protocol/src/main/scala/net/shrine/protocol/QueryMaster.scala index 2caea9b9a..ceb47358b 100644 --- a/commons/protocol/src/main/scala/net/shrine/protocol/QueryMaster.scala +++ b/commons/protocol/src/main/scala/net/shrine/protocol/QueryMaster.scala @@ -1,24 +1,24 @@ package net.shrine.protocol import javax.xml.datatype.XMLGregorianCalendar /** * @author Bill Simons * @since 4/2/12 * @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 */ final case class QueryMaster ( queryMasterId: String, //Outside of tests, this is always the networkQueryId as a string networkQueryId: Long, name: String, userId: String, groupId: String, createDate: XMLGregorianCalendar, - held: Option[Boolean] = None, //todo field should be removed, along with supporting code and tests. It's never used. +// held: Option[Boolean] = None, //todo field should be removed, along with supporting code and tests. It's never used. flagged: Option[Boolean] = None, flagMessage: Option[String] = None) \ No newline at end of file diff --git a/commons/protocol/src/main/scala/net/shrine/protocol/ReadPreviousQueriesResponse.scala b/commons/protocol/src/main/scala/net/shrine/protocol/ReadPreviousQueriesResponse.scala index b738dce6e..0157f9f74 100644 --- a/commons/protocol/src/main/scala/net/shrine/protocol/ReadPreviousQueriesResponse.scala +++ b/commons/protocol/src/main/scala/net/shrine/protocol/ReadPreviousQueriesResponse.scala @@ -1,112 +1,109 @@ package net.shrine.protocol import xml.NodeSeq import net.shrine.util.{Tries, XmlUtil, XmlDateHelper, NodeSeqEnrichments} import net.shrine.serialization.{ I2b2Unmarshaller, XmlUnmarshaller } import scala.util.Try /** * @author Bill Simons * @since 4/11/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 ReadPreviousQueriesResponse(queryMasters: Seq[QueryMaster]) extends ShrineResponse { override def i2b2MessageBody: NodeSeq = XmlUtil.stripWhitespace { DONE { for { master <- queryMasters } yield { { master.queryMasterId } { master.networkQueryId } { master.name } { master.userId } { master.groupId } { master.createDate } - { master.held.map(f => { f }).orNull } { master.flagged.map(f => { f }).orNull } { master.flagMessage.map(f => { f }).orNull } } } } override def toXml: NodeSeq = XmlUtil.stripWhitespace { { queryMasters.map { master => { master.queryMasterId } { master.networkQueryId } { master.name } { master.createDate } { master.userId } { master.groupId } - { master.held.map(f => { f }).orNull } { master.flagged.map(f => { f }).orNull } { master.flagMessage.map(f => { f }).orNull } } } } } object ReadPreviousQueriesResponse extends I2b2Unmarshaller[ReadPreviousQueriesResponse] with XmlUnmarshaller[ReadPreviousQueriesResponse] { override def fromI2b2(xml: NodeSeq): ReadPreviousQueriesResponse = { //NB: Fail fast require((xml \ "response_header" \ "result_status" \ "status" \ "@type").text == "DONE") doFromXml("query_master_id", "network_query_id", "name", "user_id", "group_id", "create_date", "held", "flagged", "flagMessage")(xml \ "message_body" \ "response" \ "query_master") } override def fromXml(xml: NodeSeq): ReadPreviousQueriesResponse = doFromXml("masterId", "networkId", "name", "userId", "groupId", "createDate", "held", "flagged", "flagMessage")(xml \ "queryMaster") private[this] def doFromXml(queryMasterIdTagName: String, networkQueryIdTagName: String, nameTagName: String, userIdTagName: String, groupIdTagName: String, createDateTagName: String, heldTagName: String, flaggedTagName: String, flagMessageTagName: String)(xml: NodeSeq): ReadPreviousQueriesResponse = { val toQueryMaster = queryMasterExtractor(queryMasterIdTagName, networkQueryIdTagName, nameTagName, userIdTagName, groupIdTagName, createDateTagName, heldTagName, flaggedTagName, flagMessageTagName) val queryMasterAttempts = xml.map(toQueryMaster) //NB: Preserve old exception-throwing behavior for now val queryMasters = Tries.sequence(queryMasterAttempts).get ReadPreviousQueriesResponse(queryMasters) } import NodeSeqEnrichments.Strictness._ private[this] val toText = (_: NodeSeq).text.trim private[this] val toLong = toText(_: NodeSeq).toLong private[this] def queryMasterExtractor(queryMasterIdTagName: String, networkQueryIdTagName: String, nameTagName: String, userIdTagName: String, groupIdTagName: String, createDateTagName: String, heldTagName: String, flaggedTagName: String, flagMessageTagName: String): NodeSeq => Try[QueryMaster] = { (querymasterXml: NodeSeq) => { for { queryMasterId <- querymasterXml.withChild(queryMasterIdTagName).map(toText) networkQueryId <- querymasterXml.withChild(networkQueryIdTagName).map(toLong) name <- querymasterXml.withChild(nameTagName).map(toText) userId <- querymasterXml.withChild(userIdTagName).map(toText) groupId <- querymasterXml.withChild(groupIdTagName).map(toText) createDateText <- querymasterXml.withChild(createDateTagName).map(toText) createDate <- XmlDateHelper.parseXmlTime(createDateText) - held = (querymasterXml \ heldTagName).headOption.map(toText(_).toBoolean) flagged = (querymasterXml \ flaggedTagName).headOption.map(toText(_).toBoolean) flagMessage = (querymasterXml \ flagMessageTagName).headOption.map(toText(_)) } yield { - QueryMaster(queryMasterId, networkQueryId, name, userId, groupId, createDate, held, flagged, flagMessage) + QueryMaster(queryMasterId, networkQueryId, name, userId, groupId, createDate, flagged, flagMessage) } } } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala index 4c362ac1a..0a4256507 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala @@ -1,152 +1,177 @@ package net.shrine.protocol -import java.util.Calendar import org.junit.Test import net.shrine.util.XmlUtil import net.shrine.util.XmlDateHelper import javax.xml.datatype.XMLGregorianCalendar /** * @author Bill Simons - * @date 4/12/11 - * @link http://cbmi.med.harvard.edu - * @link http://chip.org + * @since 4/12/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 - * @link http://www.gnu.org/licenses/lgpl.html + * @see http://www.gnu.org/licenses/lgpl.html */ final class ReadPreviousQueriesResponseTest extends ShrineResponseI2b2SerializableValidator { val queryMasterId1 = 1111111L val queryMasterId2 = 222222L val networkQueryId1 = 123455667L val networkQueryId2 = 98327592L val queryName1 = "name1" val queryName2 = "name2" val userId1 = Some("user1") val userId2 = Some("user2") val groupId1 = Some("group1") val groupId2 = Some("group2") val createDate1 = XmlDateHelper.now val createDate2 = XmlDateHelper.now - val held1 = None - val held2 = Some(true) - val flagged1 = Some(true) val flagged2 = None val flagMessage1 = Some("askldhlaksdjlkasdjklasdjl") val flagMessage2 = None - val queryMaster1 = makeQueryMaster(queryMasterId1, networkQueryId1, queryName1, userId1, groupId1, createDate1, held1, flagged1, flagMessage1) - val queryMaster2 = makeQueryMaster(queryMasterId2, networkQueryId2, queryName2, userId2, groupId2, createDate2, held2, flagged2, flagMessage2) + val queryMaster1 = makeQueryMaster(queryMasterId1, networkQueryId1, queryName1, userId1, groupId1, createDate1, flagged1, flagMessage1) + val queryMaster2 = makeQueryMaster(queryMasterId2, networkQueryId2, queryName2, userId2, groupId2, createDate2, flagged2, flagMessage2) - def makeQueryMaster(queryMasterId: Long, networkQueryId: Long, queryName: String, userId: Option[String], groupId: Option[String], createDate: XMLGregorianCalendar, held: Option[Boolean], flagged: Option[Boolean], flagMessage: Option[String]) = { - QueryMaster(String.valueOf(queryMasterId), networkQueryId, queryName, userId.get, groupId.get, createDate, held, flagged, flagMessage) + def makeQueryMaster(queryMasterId: Long, networkQueryId: Long, queryName: String, userId: Option[String], groupId: Option[String], createDate: XMLGregorianCalendar, flagged: Option[Boolean], flagMessage: Option[String]) = { + QueryMaster(String.valueOf(queryMasterId), networkQueryId, queryName, userId.get, groupId.get, createDate, flagged, flagMessage) } def messageBody = XmlUtil.stripWhitespace { DONE { queryMasterId1 } { networkQueryId1 } { queryName1 } { userId1.get } { groupId1.get } { createDate1 } { flagged1.get } { flagMessage1.get } { queryMasterId2 } { networkQueryId2 } { queryName2 } { userId2.get } { groupId2.get } { createDate2 } - { held2.get } } + //keep the held field around to be sure that old messages can be read. + def oldMessageBody = XmlUtil.stripWhitespace { + + + + DONE + + + { queryMasterId1 } + { networkQueryId1 } + { queryName1 } + { userId1.get } + { groupId1.get } + { createDate1 } + { flagged1.get } + { flagMessage1.get } + + + { queryMasterId2 } + { networkQueryId2 } + { queryName2 } + { userId2.get } + { groupId2.get } + { createDate2 } + false + + + + } + + val readPreviousQueriesResponse = XmlUtil.stripWhitespace { { queryMasterId1 } { networkQueryId1 } { queryName1 } { createDate1 } { userId1.get } { groupId1.get } { flagged1.get } { flagMessage1.get } { queryMasterId2 } { networkQueryId2 } { queryName2 } { createDate2 } { userId2.get } { groupId2.get } - { held2.get } } @Test - def testFromI2b2FailsFast { + def testFromI2b2FailsFast() { intercept[Exception] { ReadPreviousQueriesResponse.fromI2b2() } intercept[Exception] { ReadPreviousQueriesResponse.fromI2b2(ErrorResponse("foo!").toI2b2) } } @Test - def testFromXml { + def testFromXml() { val actual = ReadPreviousQueriesResponse.fromXml(readPreviousQueriesResponse) val expectedQueryMasters = Set(queryMaster1, queryMaster2) actual.queryMasters.toSet should equal(expectedQueryMasters) } @Test - def testToXml { + def testToXml() { //we compare the string versions of the xml because Scala's xml equality does not always behave properly ReadPreviousQueriesResponse(Seq(queryMaster1, queryMaster2)).toXmlString should equal(readPreviousQueriesResponse.toString) } @Test - def testFromI2b2 { + def testFromI2b2() { val translatedResponse = ReadPreviousQueriesResponse.fromI2b2(response) translatedResponse.queryMasters.toSet should equal(Set(queryMaster1, queryMaster2)) } @Test - def testToI2b2 { + def testToI2b2() { //Per-queryMaster userids and groupids //we compare the string versions of the xml because Scala's xml equality does not always behave properly val actual = ReadPreviousQueriesResponse(Seq(queryMaster1, queryMaster2)).toI2b2String val expected = response.toString actual should equal(expected) } } \ No newline at end of file diff --git a/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala b/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala index 9d8b6cf61..1e44b9cc6 100644 --- a/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala +++ b/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala @@ -1,537 +1,536 @@ package net.shrine.qep.queries import java.sql.SQLException import java.util.concurrent.TimeoutException import javax.sql.DataSource import com.typesafe.config.Config import net.shrine.audit.{NetworkQueryId, QueryName, Time, UserName} import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, ProblemSources, ProblemDigest} import net.shrine.protocol.{ResultOutputTypes, DeleteQueryRequest, RenameQueryRequest, I2b2ResultEnvelope, QueryResult, ResultOutputType, DefaultBreakdownResultOutputTypes, UnFlagQueryRequest, FlagQueryRequest, QueryMaster, ReadPreviousQueriesRequest, ReadPreviousQueriesResponse, RunQueryRequest} import net.shrine.qep.QepConfigSource import net.shrine.slick.TestableDataSourceCreator import net.shrine.util.XmlDateHelper import slick.driver.JdbcProfile import scala.collection.immutable.Iterable import scala.concurrent.duration.{Duration, DurationInt} import scala.concurrent.{Await, Future, blocking} import scala.language.postfixOps import scala.concurrent.ExecutionContext.Implicits.global import scala.xml.XML /** * DB code for the QEP's query instances and query results. * * @author david * @since 1/19/16 */ case class QepQueryDb(schemaDef:QepQuerySchema,dataSource: DataSource,timeout:Duration) extends Loggable { import schemaDef._ import jdbcProfile.api._ val database = Database.forDataSource(dataSource) def createTables() = schemaDef.createTables(database) def dropTables() = schemaDef.dropTables(database) def dbRun[R](action: DBIOAction[R, NoStream, Nothing]):R = { val future: Future[R] = database.run(action) try { blocking { Await.result(future, timeout) } } catch { case tx:TimeoutException => throw CouldNotRunDbIoActionException(dataSource,tx) } } def insertQepQuery(runQueryRequest: RunQueryRequest):Unit = { debug(s"insertQepQuery $runQueryRequest") insertQepQuery(QepQuery(runQueryRequest)) } def insertQepQuery(qepQuery: QepQuery):Unit = { dbRun(allQepQueryQuery += qepQuery) } def selectAllQepQueries:Seq[QepQuery] = { dbRun(mostRecentVisibleQepQueries.result) } //todo order def selectPreviousQueries(request: ReadPreviousQueriesRequest):ReadPreviousQueriesResponse = { val previousQueries: Seq[QepQuery] = selectPreviousQueriesByUserAndDomain(request.authn.username,request.authn.domain,request.fetchSize) val flags:Map[NetworkQueryId,QepQueryFlag] = selectMostRecentQepQueryFlagsFor(previousQueries.map(_.networkId).to[Set]) val queriesAndFlags = previousQueries.map(x => (x,flags.get(x.networkId))) ReadPreviousQueriesResponse(queriesAndFlags.map(x => x._1.toQueryMaster(x._2))) } //todo order def selectPreviousQueriesByUserAndDomain(userName: UserName, domain: String, limit:Int):Seq[QepQuery] = { dbRun(mostRecentVisibleQepQueries.filter(_.userName === userName).filter(_.userDomain === domain).take(limit).result) } def renamePreviousQuery(request:RenameQueryRequest):Unit = { val networkQueryId = request.networkQueryId dbRun( for { queryResults <- mostRecentVisibleQepQueries.filter(_.networkId === networkQueryId).result _ <- allQepQueryQuery ++= queryResults.map(_.copy(queryName = request.queryName,changeDate = System.currentTimeMillis())) } yield queryResults ) } def markDeleted(request:DeleteQueryRequest):Unit = { val networkQueryId = request.networkQueryId dbRun( for { queryResults <- mostRecentVisibleQepQueries.filter(_.networkId === networkQueryId).result _ <- allQepQueryQuery ++= queryResults.map(_.copy(deleted = true,changeDate = System.currentTimeMillis())) } yield queryResults ) } def insertQepQueryFlag(flagQueryRequest: FlagQueryRequest):Unit = { insertQepQueryFlag(QepQueryFlag(flagQueryRequest)) } def insertQepQueryFlag(unflagQueryRequest: UnFlagQueryRequest):Unit = { insertQepQueryFlag(QepQueryFlag(unflagQueryRequest)) } def insertQepQueryFlag(qepQueryFlag: QepQueryFlag):Unit = { dbRun(allQepQueryFlags += qepQueryFlag) } def selectMostRecentQepQueryFlagsFor(networkIds:Set[NetworkQueryId]):Map[NetworkQueryId,QepQueryFlag] = { val flags:Seq[QepQueryFlag] = dbRun(mostRecentQueryFlags.filter(_.networkId inSet networkIds).result) flags.map(x => x.networkQueryId -> x).toMap } def insertQepResultRow(qepQueryRow:QueryResultRow) = { dbRun(allQueryResultRows += qepQueryRow) } def insertQueryResult(networkQueryId:NetworkQueryId,result:QueryResult) = { val adapterNode = result.description.getOrElse(throw new IllegalStateException("description is empty, does not have an adapter node")) val queryResultRow = QueryResultRow(networkQueryId,result) val breakdowns: Iterable[QepQueryBreakdownResultsRow] = result.breakdowns.flatMap(QepQueryBreakdownResultsRow.breakdownRowsFor(networkQueryId,adapterNode,result.resultId,_)) val problem: Seq[QepProblemDigestRow] = result.problemDigest.map(p => QepProblemDigestRow(networkQueryId,adapterNode,p.codec,p.stampText,p.summary,p.description,p.detailsXml.toString,System.currentTimeMillis())).to[Seq] dbRun( for { _ <- allQueryResultRows += queryResultRow _ <- allBreakdownResultsRows ++= breakdowns _ <- allProblemDigestRows ++= problem } yield () ) } def selectMostRecentQepResultRowsFor(networkId:NetworkQueryId): Seq[QueryResultRow] = { dbRun(mostRecentQueryResultRows.filter(_.networkQueryId === networkId).result) } def selectMostRecentQepResultsFor(networkId:NetworkQueryId): Seq[QueryResult] = { val (queryResults, breakdowns,problems) = dbRun( for { queryResults <- mostRecentQueryResultRows.filter(_.networkQueryId === networkId).result breakdowns <- mostRecentBreakdownResultsRows.filter(_.networkQueryId === networkId).result problems <- mostRecentProblemDigestRows.filter(_.networkQueryId === networkId).result } yield (queryResults, breakdowns, problems) ) val resultIdsToI2b2ResultEnvelopes: Map[Long, Map[ResultOutputType, I2b2ResultEnvelope]] = breakdowns.groupBy(_.resultId).map(rIdToB => rIdToB._1 -> QepQueryBreakdownResultsRow.resultEnvelopesFrom(rIdToB._2)) def seqOfOneProblemRowToProblemDigest(problemSeq:Seq[QepProblemDigestRow]):ProblemDigest = { if(problemSeq.size == 1) problemSeq.head.toProblemDigest else throw new IllegalStateException(s"problemSeq size was not 1. $problemSeq") } val adapterNodesToProblemDigests: Map[String, ProblemDigest] = problems.groupBy(_.adapterNode).map(nodeToProblem => nodeToProblem._1 -> seqOfOneProblemRowToProblemDigest(nodeToProblem._2) ) queryResults.map(r => r.toQueryResult( resultIdsToI2b2ResultEnvelopes.getOrElse(r.resultId,Map.empty), adapterNodesToProblemDigests.get(r.adapterNode) )) } def insertQueryBreakdown(breakdownResultsRow:QepQueryBreakdownResultsRow) = { dbRun(allBreakdownResultsRows += breakdownResultsRow) } def selectAllBreakdownResultsRows: Seq[QepQueryBreakdownResultsRow] = { dbRun(allBreakdownResultsRows.result) } } object QepQueryDb extends Loggable { val dataSource:DataSource = TestableDataSourceCreator.dataSource(QepQuerySchema.config) val timeout = QepQuerySchema.config.getInt("timeout") seconds val db = QepQueryDb(QepQuerySchema.schema,dataSource,timeout) val createTablesOnStart = QepQuerySchema.config.getBoolean("createTablesOnStart") if(createTablesOnStart) QepQueryDb.db.createTables() } /** * Separate class to support schema generation without actually connecting to the database. * * @param jdbcProfile Database profile to use for the schema */ case class QepQuerySchema(jdbcProfile: JdbcProfile,moreBreakdowns: Set[ResultOutputType]) extends Loggable { import jdbcProfile.api._ def ddlForAllTables: jdbcProfile.DDL = { allQepQueryQuery.schema ++ allQepQueryFlags.schema ++ allQueryResultRows.schema ++ allBreakdownResultsRows.schema ++ allProblemDigestRows.schema } //to get the schema, use the REPL //println(QepQuerySchema.schema.ddlForAllTables.createStatements.mkString(";\n")) def createTables(database:Database) = { try { val future = database.run(ddlForAllTables.create) Await.result(future,10 seconds) } catch { //I'd prefer to check and create schema only if absent. No way to do that with Oracle. case x:SQLException => info("Caught exception while creating tables. Recover by assuming the tables already exist.",x) } } def dropTables(database:Database) = { val future = database.run(ddlForAllTables.drop) //Really wait forever for the cleanup Await.result(future,Duration.Inf) } class QepQueries(tag:Tag) extends Table[QepQuery](tag,"previousQueries") { def networkId = column[NetworkQueryId]("networkId") def userName = column[UserName]("userName") def userDomain = column[String]("domain") def queryName = column[QueryName]("queryName") def expression = column[String]("expression") def dateCreated = column[Time]("dateCreated") def deleted = column[Boolean]("deleted") def queryXml = column[String]("queryXml") def changeDate = column[Long]("changeDate") def * = (networkId,userName,userDomain,queryName,expression,dateCreated,deleted,queryXml,changeDate) <> (QepQuery.tupled,QepQuery.unapply) } val allQepQueryQuery = TableQuery[QepQueries] val mostRecentQepQueryQuery: Query[QepQueries, QepQuery, Seq] = for( queries <- allQepQueryQuery if !allQepQueryQuery.filter(_.networkId === queries.networkId).filter(_.changeDate > queries.changeDate).exists ) yield queries val mostRecentVisibleQepQueries = mostRecentQepQueryQuery.filter(_.deleted === false) class QepQueryFlags(tag:Tag) extends Table[QepQueryFlag](tag,"queryFlags") { def networkId = column[NetworkQueryId]("networkId") def flagged = column[Boolean]("flagged") def flagMessage = column[String]("flagMessage") def changeDate = column[Long]("changeDate") def * = (networkId,flagged,flagMessage,changeDate) <> (QepQueryFlag.tupled,QepQueryFlag.unapply) } val allQepQueryFlags = TableQuery[QepQueryFlags] val mostRecentQueryFlags: Query[QepQueryFlags, QepQueryFlag, Seq] = for( queryFlags <- allQepQueryFlags if !allQepQueryFlags.filter(_.networkId === queryFlags.networkId).filter(_.changeDate > queryFlags.changeDate).exists ) yield queryFlags val qepQueryResultTypes = DefaultBreakdownResultOutputTypes.toSet ++ ResultOutputType.values ++ moreBreakdowns val stringsToQueryResultTypes: Map[String, ResultOutputType] = qepQueryResultTypes.map(x => (x.name,x)).toMap val queryResultTypesToString: Map[ResultOutputType, String] = stringsToQueryResultTypes.map(_.swap) implicit val qepQueryResultTypesColumnType = MappedColumnType.base[ResultOutputType,String] ({ (resultType: ResultOutputType) => queryResultTypesToString(resultType) },{ (string: String) => stringsToQueryResultTypes(string) }) implicit val queryStatusColumnType = MappedColumnType.base[QueryResult.StatusType,String] ({ statusType => statusType.name },{ name => QueryResult.StatusType.valueOf(name).getOrElse(throw new IllegalStateException(s"$name is not one of ${QueryResult.StatusType.values.map(_.name).mkString(", ")}")) }) class QepQueryResults(tag:Tag) extends Table[QueryResultRow](tag,"queryResults") { def resultId = column[Long]("resultId") def networkQueryId = column[NetworkQueryId]("networkQueryId") def instanceId = column[Long]("instanceId") def adapterNode = column[String]("adapterNode") def resultType = column[ResultOutputType]("resultType") def size = column[Long]("size") def startDate = column[Option[Long]]("startDate") def endDate = column[Option[Long]]("endDate") def status = column[QueryResult.StatusType]("status") def statusMessage = column[Option[String]]("statusMessage") def changeDate = column[Long]("changeDate") def * = (resultId,networkQueryId,instanceId,adapterNode,resultType,size,startDate,endDate,status,statusMessage,changeDate) <> (QueryResultRow.tupled,QueryResultRow.unapply) } val allQueryResultRows = TableQuery[QepQueryResults] //Most recent query result rows for each queryId from each adapter val mostRecentQueryResultRows: Query[QepQueryResults, QueryResultRow, Seq] = for( queryResultRows <- allQueryResultRows if !allQueryResultRows.filter(_.networkQueryId === queryResultRows.networkQueryId).filter(_.adapterNode === queryResultRows.adapterNode).filter(_.changeDate > queryResultRows.changeDate).exists ) yield queryResultRows class QepQueryBreakdownResults(tag:Tag) extends Table[QepQueryBreakdownResultsRow](tag,"queryBreakdownResults") { def networkQueryId = column[NetworkQueryId]("networkQueryId") def adapterNode = column[String]("adapterNode") def resultId = column[Long]("resultId") def resultType = column[ResultOutputType]("resultType") def dataKey = column[String]("dataKey") def value = column[Long]("value") def changeDate = column[Long]("changeDate") def * = (networkQueryId,adapterNode,resultId,resultType,dataKey,value,changeDate) <> (QepQueryBreakdownResultsRow.tupled,QepQueryBreakdownResultsRow.unapply) } val allBreakdownResultsRows = TableQuery[QepQueryBreakdownResults] //Most recent query result rows for each queryId from each adapter val mostRecentBreakdownResultsRows: Query[QepQueryBreakdownResults, QepQueryBreakdownResultsRow, Seq] = for( breakdownResultsRows <- allBreakdownResultsRows if !allBreakdownResultsRows.filter(_.networkQueryId === breakdownResultsRows.networkQueryId).filter(_.adapterNode === breakdownResultsRows.adapterNode).filter(_.resultId === breakdownResultsRows.resultId).filter(_.changeDate > breakdownResultsRows.changeDate).exists ) yield breakdownResultsRows /* case class ProblemDigest(codec: String, stampText: String, summary: String, description: String, detailsXml: NodeSeq) extends XmlMarshaller { */ class QepResultProblemDigests(tag:Tag) extends Table [QepProblemDigestRow](tag,"queryResultProblemDigests") { def networkQueryId = column[NetworkQueryId]("networkQueryId") def adapterNode = column[String]("adapterNode") def codec = column[String]("codec") def stamp = column[String]("stamp") def summary = column[String]("summary") def description = column[String]("description") def details = column[String]("details") def changeDate = column[Long]("changeDate") def * = (networkQueryId,adapterNode,codec,stamp,summary,description,details,changeDate) <> (QepProblemDigestRow.tupled,QepProblemDigestRow.unapply) } val allProblemDigestRows = TableQuery[QepResultProblemDigests] val mostRecentProblemDigestRows: Query[QepResultProblemDigests, QepProblemDigestRow, Seq] = for( problemDigests <- allProblemDigestRows if !allProblemDigestRows.filter(_.networkQueryId === problemDigests.networkQueryId).filter(_.adapterNode === problemDigests.adapterNode).filter(_.changeDate > problemDigests.changeDate).exists ) yield problemDigests } object QepQuerySchema { val allConfig:Config = QepConfigSource.config val config:Config = allConfig.getConfig("shrine.queryEntryPoint.audit.database") val slickProfileClassName = config.getString("slickProfileClassName") val slickProfile:JdbcProfile = QepConfigSource.objectForName(slickProfileClassName) import net.shrine.config.{ConfigExtensions, Keys} val moreBreakdowns: Set[ResultOutputType] = config.getOptionConfigured(Keys.breakdownResultOutputTypes,ResultOutputTypes.fromConfig).getOrElse(Set.empty) val schema = QepQuerySchema(slickProfile,moreBreakdowns) } case class QepQuery( networkId:NetworkQueryId, userName: UserName, userDomain: String, queryName: QueryName, expression: String, dateCreated: Time, deleted: Boolean, queryXml: String, changeDate: Time ){ def toQueryMaster(qepQueryFlag:Option[QepQueryFlag]):QueryMaster = { QueryMaster( queryMasterId = networkId.toString, networkQueryId = networkId, name = queryName, userId = userName, groupId = userDomain, createDate = XmlDateHelper.toXmlGregorianCalendar(dateCreated), - held = None, //todo this field is never used. Remove it in 1.22 flagged = qepQueryFlag.map(_.flagged), flagMessage = qepQueryFlag.map(_.flagMessage) ) } } object QepQuery extends ((NetworkQueryId,UserName,String,QueryName,String,Time,Boolean,String,Time) => QepQuery) { def apply(runQueryRequest: RunQueryRequest):QepQuery = { new QepQuery( networkId = runQueryRequest.networkQueryId, userName = runQueryRequest.authn.username, userDomain = runQueryRequest.authn.domain, queryName = runQueryRequest.queryDefinition.name, expression = runQueryRequest.queryDefinition.expr.getOrElse("No Expression").toString, dateCreated = System.currentTimeMillis(), deleted = false, queryXml = runQueryRequest.toXmlString, changeDate = System.currentTimeMillis() ) } } case class QepQueryFlag( networkQueryId: NetworkQueryId, flagged:Boolean, flagMessage:String, changeDate:Long ) object QepQueryFlag extends ((NetworkQueryId,Boolean,String,Long) => QepQueryFlag) { def apply(flagQueryRequest: FlagQueryRequest):QepQueryFlag = { QepQueryFlag( networkQueryId = flagQueryRequest.networkQueryId, flagged = true, flagMessage = flagQueryRequest.message.getOrElse(""), changeDate = System.currentTimeMillis() ) } def apply(unflagQueryRequest: UnFlagQueryRequest):QepQueryFlag = { QepQueryFlag( networkQueryId = unflagQueryRequest.networkQueryId, flagged = false, flagMessage = "", changeDate = System.currentTimeMillis() ) } } case class QueryResultRow( resultId:Long, networkQueryId:NetworkQueryId, instanceId:Long, adapterNode:String, resultType:ResultOutputType, size:Long, startDate:Option[Long], endDate:Option[Long], status:QueryResult.StatusType, statusMessage:Option[String], changeDate:Long ) { def toQueryResult(breakdowns:Map[ResultOutputType,I2b2ResultEnvelope],problemDigest:Option[ProblemDigest]) = QueryResult( resultId = resultId, instanceId = instanceId, resultType = Some(resultType), setSize = size, startDate = startDate.map(XmlDateHelper.toXmlGregorianCalendar), endDate = endDate.map(XmlDateHelper.toXmlGregorianCalendar), description = Some(adapterNode), statusType = status, statusMessage = statusMessage, breakdowns = breakdowns, problemDigest = problemDigest ) } object QueryResultRow extends ((Long,NetworkQueryId,Long,String,ResultOutputType,Long,Option[Long],Option[Long],QueryResult.StatusType,Option[String],Long) => QueryResultRow) { def apply(networkQueryId:NetworkQueryId,result:QueryResult):QueryResultRow = { new QueryResultRow( resultId = result.resultId, networkQueryId = networkQueryId, instanceId = result.instanceId, adapterNode = result.description.getOrElse(s"$result has None in its description field, not a name of an adapter node."), resultType = result.resultType.getOrElse(ResultOutputType.PATIENT_COUNT_XML), //todo how is this optional?? size = result.setSize, startDate = result.startDate.map(_.toGregorianCalendar.getTimeInMillis), endDate = result.endDate.map(_.toGregorianCalendar.getTimeInMillis), status = result.statusType, statusMessage = result.statusMessage, changeDate = System.currentTimeMillis() ) } } case class QepQueryBreakdownResultsRow( networkQueryId: NetworkQueryId, adapterNode:String, resultId:Long, resultType: ResultOutputType, dataKey:String, value:Long, changeDate:Long ) object QepQueryBreakdownResultsRow extends ((NetworkQueryId,String,Long,ResultOutputType,String,Long,Long) => QepQueryBreakdownResultsRow){ def breakdownRowsFor(networkQueryId:NetworkQueryId, adapterNode:String, resultId:Long, breakdown:(ResultOutputType,I2b2ResultEnvelope)): Iterable[QepQueryBreakdownResultsRow] = { breakdown._2.data.map(b => QepQueryBreakdownResultsRow(networkQueryId,adapterNode,resultId,breakdown._1,b._1,b._2,System.currentTimeMillis())) } def resultEnvelopesFrom(breakdowns:Seq[QepQueryBreakdownResultsRow]): Map[ResultOutputType, I2b2ResultEnvelope] = { def resultEnvelopeFrom(resultType:ResultOutputType,breakdowns:Seq[QepQueryBreakdownResultsRow]):I2b2ResultEnvelope = { val data = breakdowns.map(b => b.dataKey -> b.value).toMap I2b2ResultEnvelope(resultType,data) } breakdowns.groupBy(_.resultType).map(r => r._1 -> resultEnvelopeFrom(r._1,r._2)) } } case class QepProblemDigestRow( networkQueryId: NetworkQueryId, adapterNode: String, codec: String, stampText: String, summary: String, description: String, details: String, changeDate:Long ){ def toProblemDigest = { ProblemDigest( codec, stampText, summary, description, if(!details.isEmpty) XML.loadString(details) else

) } } case class CouldNotRunDbIoActionException(dataSource: DataSource, exception: Exception) extends RuntimeException(exception) { override def getMessage:String = s"Could not use the database defined by $dataSource due to ${exception.getLocalizedMessage}" } case class QepDatabaseProblem(x:Exception) extends AbstractProblem(ProblemSources.Qep){ override val summary = "A problem encountered while using a database." override val throwable = Some(x) override val description = x.getMessage() } \ No newline at end of file