diff --git a/README.md b/README.md index 9e5b9acd..5932414d 100644 --- a/README.md +++ b/README.md @@ -300,6 +300,10 @@ Now you can disable root by setting `api.root.enabled` to `false` in `/etc/horiz - detect if a pattern is updated with service that has userInput w/o default values, and give warning - Consider changing all creates to POST, and update (via put/patch) return codes to 200 +## Changes in 2.101.0 +- Issue 581: Array order for versions maintained in the Exchange's DB and returned correctly when retrieved using a GET. +- Issue 582: Nodes, Agbots, Users, and Admin can now read resource changes for AgentFileVersions. + ## Changes in 2.100.0 - Issue 572: All request body values are optional except for `scheduledTime`. - Updated internationalization. diff --git a/docs/openapi-3-developer.json b/docs/openapi-3-developer.json index fa39c339..e310ed82 100644 --- a/docs/openapi-3-developer.json +++ b/docs/openapi-3-developer.json @@ -8,7 +8,7 @@ "name" : "Apache License Version 2.0", "url" : "https://www.apache.org/licenses/LICENSE-2.0" }, - "version" : "2.100.0" + "version" : "2.101.0" }, "externalDocs" : { "description" : "Open-horizon ExchangeAPI", diff --git a/docs/openapi-3-user.json b/docs/openapi-3-user.json index 5b202925..93750273 100644 --- a/docs/openapi-3-user.json +++ b/docs/openapi-3-user.json @@ -8,7 +8,7 @@ "name" : "Apache License Version 2.0", "url" : "https://www.apache.org/licenses/LICENSE-2.0" }, - "version" : "2.100.0" + "version" : "2.101.0" }, "externalDocs" : { "description" : "Open-horizon ExchangeAPI", diff --git a/src/main/resources/version.txt b/src/main/resources/version.txt index e24025aa..e5e8eb7c 100644 --- a/src/main/resources/version.txt +++ b/src/main/resources/version.txt @@ -1 +1 @@ -2.100.0 +2.101.0 diff --git a/src/main/scala/com/horizon/exchangeapi/AgentConfigurationManagementRoutes.scala b/src/main/scala/com/horizon/exchangeapi/AgentConfigurationManagementRoutes.scala index 7464f6ee..6246dfaa 100644 --- a/src/main/scala/com/horizon/exchangeapi/AgentConfigurationManagementRoutes.scala +++ b/src/main/scala/com/horizon/exchangeapi/AgentConfigurationManagementRoutes.scala @@ -76,8 +76,8 @@ trait AgentConfigurationManagementRoutes extends JacksonSupport with Authenticat lastUpdated = timestamp, operation = ResChangeOperation.MODIFIED.toString, orgId = "IBM", - public = "false", - resource = ResChangeResource.ORG.toString) + public = "true", + resource = ResChangeResource.AGENTFILEVERSION.toString) software <- AgentSoftwareVersionsTQ.delete } yield (certificate, changed, configuration, resource, software) @@ -150,10 +150,10 @@ trait AgentConfigurationManagementRoutes extends JacksonSupport with Authenticat db.run({ val versions: DBIOAction[(Seq[String], Seq[Timestamp], Seq[String], Seq[String]), NoStream, Effect.Read] = for { - certificate <- AgentCertificateVersionsTQ.getAgentCertificateVersions("IBM").sortBy(_.desc).result + certificate <- AgentCertificateVersionsTQ.sortBy(_.priority.asc.nullsLast).filter(_.organization === "IBM").map(_.certificateVersion).result changed <- AgentVersionsChangedTQ.getChanged("IBM").sortBy(_.desc).result - configuration <- AgentConfigurationVersionsTQ.getAgentConfigurationVersions("IBM").sortBy(_.desc).result - software <- AgentSoftwareVersionsTQ.getAgentSoftwareVersions("IBM").sortBy(_.desc).result + configuration <- AgentConfigurationVersionsTQ.sortBy(_.priority.asc.nullsLast).filter(_.organization === "IBM").map(_.configurationVersion).result + software <- AgentSoftwareVersionsTQ.sortBy(_.priority.asc.nullsLast).filter(_.organization === "IBM").map(_.softwareVersion).result } yield (certificate, changed, configuration, software) versions.transactionally.asTry}).map({ @@ -220,31 +220,22 @@ trait AgentConfigurationManagementRoutes extends JacksonSupport with Authenticat def putAgentConfigMgmt: Route = (path("orgs" / Segment / "AgentFileVersion") & put & entity(as[AgentVersionsRequest])) { (orgId, reqBody) => exchAuth(TOrg("IBM"), Access.WRITE_AGENT_CONFIG_MGMT) { _ => complete({ - val a: Seq[String] = reqBody.agentCertVersions - val b: Seq[String] = reqBody.agentConfigVersions - val c: Seq[String] = reqBody.agentSoftwareVersions orgId match { case "IBM" => - db.run( - (AgentCertificateVersionsTQ.delete) andThen (AgentConfigurationVersionsTQ.delete) andThen (AgentSoftwareVersionsTQ.delete) - andThen ( - AgentCertificateVersionsTQ ++= a.map(v => {(v, orgId)})) - andThen ( - AgentConfigurationVersionsTQ ++= b.map(v => {(v, orgId)})) - andThen ( - AgentSoftwareVersionsTQ ++= c.map(v => {(orgId, v)})) - andThen ( - AgentVersionsChangedTQ - .insertOrUpdate(ApiTime.nowUTCTimestamp, orgId)) - andThen ( - ResourceChange(category = ResChangeCategory.ORG, - changeId = 0L, - id = orgId, - operation = ResChangeOperation.MODIFIED, - orgId = orgId, - public = false, - resource = ResChangeResource.ORG) - .insert) + db.run((AgentCertificateVersionsTQ.delete) andThen + (AgentConfigurationVersionsTQ.delete) andThen + (AgentSoftwareVersionsTQ.delete) andThen + (AgentCertificateVersionsTQ ++= reqBody.agentCertVersions.zipWithIndex.map(certificates => {(certificates._1, orgId, Option(certificates._2.toLong))})) andThen + (AgentConfigurationVersionsTQ ++= reqBody.agentConfigVersions.zipWithIndex.map(configurations => {(configurations._1, orgId, Option(configurations._2.toLong))})) andThen + (AgentSoftwareVersionsTQ ++= reqBody.agentSoftwareVersions.zipWithIndex.map(software => {(orgId, software._1, Option(software._2.toLong))})) andThen + (AgentVersionsChangedTQ.insertOrUpdate((ApiTime.nowUTCTimestamp, orgId))) andThen + (ResourceChange(category = ResChangeCategory.ORG, + changeId = 0L, + id = orgId, + operation = ResChangeOperation.MODIFIED, + orgId = orgId, + public = true, + resource = ResChangeResource.AGENTFILEVERSION).insert) .transactionally.asTry.map({ case Success(v) => logger.debug("PUT /orgs/" + orgId + "/AgentFileVersion result: " + v) diff --git a/src/main/scala/com/horizon/exchangeapi/tables/AgentConfigurationManagement.scala b/src/main/scala/com/horizon/exchangeapi/tables/AgentConfigurationManagement.scala index 1eced583..aaf1b4ce 100644 --- a/src/main/scala/com/horizon/exchangeapi/tables/AgentConfigurationManagement.scala +++ b/src/main/scala/com/horizon/exchangeapi/tables/AgentConfigurationManagement.scala @@ -26,13 +26,15 @@ final case class AgentVersionsResponse(agentCertVersions: Seq[String], lastUpdated: String) extends AgentVersions -class AgentCertificateVersions(tag: Tag) extends Table[(String, String)](tag, "agent_version_certificate") { +class AgentCertificateVersions(tag: Tag) extends Table[(String, String, Option[Long])](tag, "agent_version_certificate") { def certificateVersion = column[String]("version") def organization = column[String]("organization", O.Default("IBM")) + def priority = column[Option[Long]]("priority") - def * = (certificateVersion, organization) + def * = (certificateVersion, organization, priority) def pkAgentVerCert = primaryKey("pk_agent_version_certificate", (organization, certificateVersion)) def fkOrg = foreignKey("fk_organization", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) + def idxPriority = index("idx_avcert_priority", (organization, priority), unique = true) } object AgentCertificateVersionsTQ extends TableQuery(new AgentCertificateVersions(_)) { @@ -40,13 +42,15 @@ object AgentCertificateVersionsTQ extends TableQuery(new AgentCertificateVersion } -class AgentConfigurationVersions(tag: Tag) extends Table[(String, String)](tag, "agent_version_configuration") { +class AgentConfigurationVersions(tag: Tag) extends Table[(String, String, Option[Long])](tag, "agent_version_configuration") { def configurationVersion = column[String]("version") def organization = column[String]("organization", O.Default("IBM")) + def priority = column[Option[Long]]("priority") - def * = (configurationVersion, organization) + def * = (configurationVersion, organization, priority) def pkAgentVerConfig = primaryKey("pk_agent_version_configuration", (organization, configurationVersion)) def fkOrg = foreignKey("fk_organization", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) + def idxPriority = index("idx_avconfig_priority", (organization, priority), unique = true) } object AgentConfigurationVersionsTQ extends TableQuery(new AgentConfigurationVersions(_)) { @@ -54,13 +58,15 @@ object AgentConfigurationVersionsTQ extends TableQuery(new AgentConfigurationVer } -class AgentSoftwareVersions(tag: Tag) extends Table[(String, String)](tag, "agent_version_software") { +class AgentSoftwareVersions(tag: Tag) extends Table[(String, String, Option[Long])](tag, "agent_version_software") { def organization = column[String]("organization", O.Default("IBM")) def softwareVersion = column[String]("version") + def priority = column[Option[Long]]("priority") - def * = (organization, softwareVersion) + def * = (organization, softwareVersion, priority) def pkAgentVerSoft = primaryKey("pk_agent_version_software", (organization, softwareVersion)) def fkOrg = foreignKey("fk_organization", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) + def idxPriority = index("idx_avsoft_priority", (organization, priority), unique = true) } object AgentSoftwareVersionsTQ extends TableQuery(new AgentSoftwareVersions(_)) { diff --git a/src/main/scala/com/horizon/exchangeapi/tables/Orgs.scala b/src/main/scala/com/horizon/exchangeapi/tables/Orgs.scala index 1d4ac80f..50903bc2 100644 --- a/src/main/scala/com/horizon/exchangeapi/tables/Orgs.scala +++ b/src/main/scala/com/horizon/exchangeapi/tables/Orgs.scala @@ -1,15 +1,14 @@ package com.horizon.exchangeapi.tables import java.sql.Timestamp - import org.json4s._ import org.json4s.jackson.Serialization.read import org.json4s.jackson.Serialization.write import slick.dbio.Effect import slick.sql.FixedSqlAction - -import com.horizon.exchangeapi.{ApiUtils,ApiTime} +import com.horizon.exchangeapi.{ApiTime, ApiUtils} import com.horizon.exchangeapi.tables.ExchangePostgresProfile.api._ +import com.horizon.exchangeapi.tables.ResChangeCategory.Value /** Contains the object representations of the DB tables related to orgs. */ @@ -102,12 +101,12 @@ final case class Org(orgType: String, label: String, description: String, lastUp /** Contains the object representations of the DB tables related to resource changes. */ object ResChangeCategory extends Enumeration { type ResChangeCategory = Value - val ORG: ResChangeCategory.Value = Value("org") val AGBOT: ResChangeCategory.Value = Value("agbot") - val NODE: ResChangeCategory.Value = Value("node") - val POLICY: ResChangeCategory.Value = Value("policy") val MGMTPOLICY: ResChangeCategory.Value = Value("mgmtpolicy") + val NODE: ResChangeCategory.Value = Value("node") + val ORG: ResChangeCategory.Value = Value("org") val PATTERN: ResChangeCategory.Value = Value("pattern") + val POLICY: ResChangeCategory.Value = Value("policy") val SERVICE: ResChangeCategory.Value = Value("service") } import com.horizon.exchangeapi.tables.ResChangeCategory._ @@ -119,6 +118,7 @@ object ResChangeResource extends Enumeration { val AGBOTBUSINESSPOLS: ResChangeResource.Value = Value("agbotbusinesspols") val AGBOTMSGS: ResChangeResource.Value = Value("agbotmsgs") val AGBOTPATTERNS: ResChangeResource.Value = Value("agbotpatterns") + val AGENTFILEVERSION: ResChangeResource.Value = Value("agentfileversion") val NODE: ResChangeResource.Value = Value("node") val NODEAGREEMENTS: ResChangeResource.Value = Value("nodeagreements") val NODEERRORS: ResChangeResource.Value = Value("nodeerrors") diff --git a/src/main/scala/com/horizon/exchangeapi/tables/Schema.scala b/src/main/scala/com/horizon/exchangeapi/tables/Schema.scala index 26957b95..b009ccdd 100644 --- a/src/main/scala/com/horizon/exchangeapi/tables/Schema.scala +++ b/src/main/scala/com/horizon/exchangeapi/tables/Schema.scala @@ -204,7 +204,7 @@ object SchemaTQ extends TableQuery(new SchemaTable(_)){ AgentSoftwareVersionsTQ.schema.create, AgentVersionsChangedTQ.schema.create ) - case 47 => DBIO.seq( // v2..0 + case 47 => DBIO.seq( // v2.100.0 sqlu"ALTER TABLE management_policy_status_node ALTER COLUMN time_start_actual DROP NOT NULL", sqlu"ALTER TABLE management_policy_status_node ALTER COLUMN version_certificate DROP NOT NULL", sqlu"ALTER TABLE management_policy_status_node ALTER COLUMN version_configuration DROP NOT NULL", @@ -212,13 +212,21 @@ object SchemaTQ extends TableQuery(new SchemaTable(_)){ sqlu"ALTER TABLE management_policy_status_node ALTER COLUMN error_message DROP NOT NULL", sqlu"ALTER TABLE management_policy_status_node ALTER COLUMN version_software DROP NOT NULL", sqlu"ALTER TABLE management_policy_status_node ALTER COLUMN status DROP NOT NULL" + ) + case 48 => DBIO.seq( // v2.101.0 + sqlu"ALTER TABLE agent_version_certificate ADD COLUMN IF NOT EXISTS priority bigint NULL;", + sqlu"ALTER TABLE agent_version_configuration ADD COLUMN IF NOT EXISTS priority bigint NULL;", + sqlu"ALTER TABLE agent_version_software ADD COLUMN IF NOT EXISTS priority bigint NULL;", + sqlu"CREATE UNIQUE INDEX IF NOT EXISTS idx_avcert_priority ON agent_version_certificate (organization, priority);", + sqlu"CREATE UNIQUE INDEX IF NOT EXISTS idx_avconfig_priority ON agent_version_configuration (organization, priority);", + sqlu"""CREATE UNIQUE INDEX IF NOT EXISTS idx_avsoft_priority ON agent_version_software ("version", priority);""" ) case other => logger.error("getUpgradeSchemaStep was given invalid step "+other); DBIO.seq() // should never get here } } - val latestSchemaVersion = 47 // NOTE: THIS MUST BE CHANGED WHEN YOU ADD TO getUpgradeSchemaStep() above - val latestSchemaDescription = "adding deployment, management, nodepolicyversion columns to nodepolicies table" + val latestSchemaVersion: Int = 48 // NOTE: THIS MUST BE CHANGED WHEN YOU ADD TO getUpgradeSchemaStep() above + val latestSchemaDescription: String = "adding deployment, management, nodepolicyversion columns to nodepolicies table" // Note: if you need to manually set the schema number in the db lower: update schema set schemaversion = 12 where id = 0; def isLatestSchemaVersion(fromSchemaVersion: Int): Boolean = fromSchemaVersion >= latestSchemaVersion diff --git a/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestDeleteAgentConfigMgmt.scala b/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestDeleteAgentConfigMgmt.scala index fe41a459..de823565 100644 --- a/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestDeleteAgentConfigMgmt.scala +++ b/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestDeleteAgentConfigMgmt.scala @@ -1,7 +1,7 @@ package com.horizon.exchangeapi.route.agentconfigurationmanagement -import com.horizon.exchangeapi.tables.{AgbotMsgRow, AgbotMsgsTQ, AgbotRow, AgbotsTQ, AgentCertificateVersionsTQ, AgentConfigurationVersionsTQ, AgentSoftwareVersionsTQ, AgentVersionsChangedTQ, AgentVersionsResponse, NodeRow, NodesTQ, OrgRow, OrgsTQ, ResChangeCategory, ResChangeOperation, ResourceChangeRow, ResourceChangesTQ, SearchOffsetPolicyAttributes, SearchOffsetPolicyTQ, UserRow, UsersTQ} -import com.horizon.exchangeapi.{ApiTime, ApiUtils, GetAgbotMsgsResponse, HttpCode, PatchOrgRequest, PostPutUsersRequest, PutAgbotsRequest, Role, TestDBConnection} +import com.horizon.exchangeapi.tables.{AgbotMsgRow, AgbotMsgsTQ, AgbotRow, AgbotsTQ, AgentCertificateVersionsTQ, AgentConfigurationVersionsTQ, AgentSoftwareVersionsTQ, AgentVersionsChangedTQ, AgentVersionsResponse, NodeRow, NodesTQ, OrgRow, OrgsTQ, ResChangeCategory, ResChangeOperation, ResChangeResource, ResourceChangeRow, ResourceChangesTQ, SearchOffsetPolicyAttributes, SearchOffsetPolicyTQ, UserRow, UsersTQ} +import com.horizon.exchangeapi.{ApiTime, ApiUtils, ChangeEntry, GetAgbotMsgsResponse, HttpCode, PatchOrgRequest, PostPutUsersRequest, PutAgbotsRequest, ResourceChangesRequest, ResourceChangesRespObject, Role, TestDBConnection} import org.json4s.DefaultFormats import org.json4s.jackson.JsonMethods.parse import org.json4s.native.Serialization.write @@ -10,21 +10,22 @@ import org.scalatest.funsuite.AnyFunSuite import scalaj.http.{Http, HttpResponse} import slick.jdbc.PostgresProfile.api._ +import java.sql.Timestamp import java.time.ZoneId import scala.concurrent.Await import scala.concurrent.duration.{Duration, DurationInt} @DoNotDiscover class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Suite { - private val ACCEPT = ("Accept","application/json") + private val ACCEPT: (String, String) = ("Accept","application/json") private val CONTENT = ("Content-Type","application/json") private val AWAITDURATION: Duration = 15.seconds private val DBCONNECTION: TestDBConnection = new TestDBConnection // private val ORGID = "TestDeleteAgentConfigMgmt" - private val ROOTAUTH = ("Authorization","Basic " + ApiUtils.encode(Role.superUser + ":" + sys.env.getOrElse("EXCHANGE_ROOTPW", ""))) - private val URL = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" + private val ROOTAUTH: (String, String) = ("Authorization", "Basic " + ApiUtils.encode(Role.superUser + ":" + sys.env.getOrElse("EXCHANGE_ROOTPW", ""))) + private val URL: String = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" - private implicit val formats = DefaultFormats + private implicit val formats: DefaultFormats.type = DefaultFormats private val TESTAGBOT: AgbotRow = AgbotRow(id = "TestDeleteAgentConfigMgmt/a1", @@ -70,7 +71,9 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with } override def afterAll(): Unit = { - Await.ready(DBCONNECTION.getDb.run(ResourceChangesTQ.filter(_.orgId startsWith "TestDeleteAgentConfigMgmt").delete andThen + Await.ready(DBCONNECTION.getDb.run(ResourceChangesTQ.filter(change => {(change.orgId startsWith "TestPutAgentConfigMgmt") || + (change.orgId === "IBM" && + change.resource === "agentfileversion")}).delete andThen OrgsTQ.filter(_.orgid startsWith "TestDeleteAgentConfigMgmt").delete), AWAITDURATION) DBCONNECTION.getDb.close() @@ -88,7 +91,7 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with } // Agent Certificate Versions that are dynamically needed, specific to the test case. - def fixtureAgentCertVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentCertVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try{ Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -98,7 +101,7 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with } // Agent Configuration Versions that are dynamically needed, specific to the test case. - def fixtureAgentConfigVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentConfigVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try{ Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -108,7 +111,7 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with } // Agent Software Versions that are dynamically needed, specific to the test case. - def fixtureAgentSoftVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentSoftVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try{ Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -197,13 +200,13 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with } test("DELETE /v1/orgs/IBM/AgentFileVersion -- 204 Deleted - Root") { - val TESTCERT: Seq[(String, String)] = - Seq(("1.1.1", "IBM")) - val TESTCONFIG: Seq[(String, String)] = - Seq(("2.2.2", "IBM")) - val TESTSOFT: Seq[(String, String)] = - Seq(("IBM", "3.3.3")) - val TESTCHG: Seq[(java.sql.Timestamp, String)] = + val TESTCERT: Seq[(String, String, Option[Long])] = + Seq(("1.1.1", "IBM", None)) + val TESTCONFIG: Seq[(String, String, Option[Long])] = + Seq(("2.2.2", "IBM", None)) + val TESTSOFT: Seq[(String, String, Option[Long])] = + Seq(("IBM", "3.3.3", None)) + val TESTCHG: Seq[(Timestamp, String)] = Seq((ApiTime.nowTimestamp, "IBM")) fixtureAgentCertVersions( @@ -220,9 +223,9 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with assert(response.code === HttpCode.DELETED.intValue) - val certificates: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) + val certificates: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) val changed: Seq[(java.sql.Timestamp, String)] = Await.result(DBCONNECTION.getDb.run(AgentVersionsChangedTQ.result), AWAITDURATION) - val configurations: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) + val configurations: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) val resource: Seq[ResourceChangeRow] = Await.result( DBCONNECTION.getDb.run( @@ -230,11 +233,11 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with .filter(_.id === "IBM") .filter(_.operation === ResChangeOperation.MODIFIED.toString) .filter(_.orgId === "IBM") - .filter(_.resource === ResChangeCategory.ORG.toString) + .filter(_.resource === ResChangeResource.AGENTFILEVERSION.toString) .sortBy(_.changeId.desc) .result ), AWAITDURATION) - val software: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) + val software: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) assert(certificates.isEmpty) assert(changed.length === 1) @@ -243,6 +246,19 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with assert(software.isEmpty) assert(changed(0) !== TESTCHG(0)._1) + + val request2: HttpResponse[String] = Http(URL + "TestDeleteAgentConfigMgmt/changes").postData(write(ResourceChangesRequest(changeId = 0, lastUpdated = None, maxRecords = 1000, orgList = None))).method("post").headers(CONTENT).headers(ACCEPT).headers(("Authorization","Basic " + ApiUtils.encode("TestDeleteAgentConfigMgmt/u1:u1pw"))).asString + info("code: " + request2.code) + info("body: " + request2.body) + + assert(request2.code === HttpCode.POST_OK.intValue) + + val versionChanges: List[ChangeEntry] = parse(request2.body).extract[ResourceChangesRespObject].changes.filter(change => {change.orgId === "IBM" && change.resource === "agentfileversion"}) + + assert(versionChanges.head.id === "IBM") + assert(versionChanges.head.operation === "modified") + assert(versionChanges.head.orgId === "IBM") + assert(versionChanges.head.resource === "agentfileversion") }, TESTCHG) }, TESTSOFT) }, TESTCONFIG) @@ -272,20 +288,20 @@ class TestDeleteAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with assert(response.code === HttpCode.DELETED.intValue) - val certificates: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) - val changed: Seq[(java.sql.Timestamp, String)] = Await.result(DBCONNECTION.getDb.run(AgentVersionsChangedTQ.result), AWAITDURATION) - val configurations: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) + val certificates: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) + val changed: Seq[(Timestamp, String)] = Await.result(DBCONNECTION.getDb.run(AgentVersionsChangedTQ.result), AWAITDURATION) + val configurations: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) val resource: Seq[ResourceChangeRow] = Await.result(DBCONNECTION.getDb.run( ResourceChangesTQ.filter(_.category === ResChangeCategory.ORG.toString) .filter(_.id === "IBM") .filter(_.operation === ResChangeOperation.MODIFIED.toString) .filter(_.orgId === "IBM") - .filter(_.resource === ResChangeCategory.ORG.toString) + .filter(_.resource === ResChangeResource.AGENTFILEVERSION.toString) .sortBy(_.changeId.desc) .result ), AWAITDURATION) - val software: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) + val software: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) assert(certificates.isEmpty) assert(changed.length === 1) diff --git a/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestGetAgentConfigMgmt.scala b/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestGetAgentConfigMgmt.scala index 80d502fb..9585a484 100644 --- a/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestGetAgentConfigMgmt.scala +++ b/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestGetAgentConfigMgmt.scala @@ -17,15 +17,15 @@ import scala.concurrent.duration.{Duration, DurationInt} @DoNotDiscover class TestGetAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Suite { - private val ACCEPT = ("Accept","application/json") - private val CONTENT = ("Content-Type","application/json") + private val ACCEPT: (String, String) = ("Accept","application/json") + private val CONTENT: (String, String) = ("Content-Type","application/json") private val AWAITDURATION: Duration = 15.seconds private val DBCONNECTION: TestDBConnection = new TestDBConnection // private val ORGID = "TestGetAgentConfigMgmt" - private val ROOTAUTH = ("Authorization","Basic " + ApiUtils.encode(Role.superUser + ":" + sys.env.getOrElse("EXCHANGE_ROOTPW", ""))) - private val URL = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" + private val ROOTAUTH: (String, String) = ("Authorization", "Basic " + ApiUtils.encode(Role.superUser + ":" + sys.env.getOrElse("EXCHANGE_ROOTPW", ""))) + private val URL: String = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" - private implicit val formats = DefaultFormats + private implicit val formats: DefaultFormats.type = DefaultFormats private val TESTAGBOT: AgbotRow = AgbotRow(id = "TestGetAgentConfigMgmt/a1", @@ -88,7 +88,9 @@ class TestGetAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Sui } override def afterAll(): Unit = { - Await.ready(DBCONNECTION.getDb.run(ResourceChangesTQ.filter(_.orgId startsWith "TestGetAgentConfigMgmt").delete andThen + Await.ready(DBCONNECTION.getDb.run(ResourceChangesTQ.filter(change => {(change.orgId startsWith "TestPutAgentConfigMgmt") || + (change.orgId === "IBM" && + change.resource === "agentfileversion")}).delete andThen OrgsTQ.filter(_.orgid startsWith "TestGetAgentConfigMgmt").delete), AWAITDURATION) DBCONNECTION.getDb.close() @@ -106,7 +108,7 @@ class TestGetAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Sui } // Agent Certificate Versions that are dynamically needed, specific to the test case. - def fixtureAgentCertVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentCertVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try{ Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -116,7 +118,7 @@ class TestGetAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Sui } // Agent Configuration Versions that are dynamically needed, specific to the test case. - def fixtureAgentConfigVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentConfigVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try{ Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -126,7 +128,7 @@ class TestGetAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Sui } // Agent Software Versions that are dynamically needed, specific to the test case. - def fixtureAgentSoftVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentSoftVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try{ Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -183,12 +185,15 @@ class TestGetAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Sui } test("GET /v1/orgs/IBM/AgentFileVersion -- 200 Ok - Root") { - val TESTCERT: Seq[(String, String)] = - Seq(("1.1.1", "IBM"), ("some other version", "IBM")) - val TESTCONFIG: Seq[(String, String)] = - Seq(("second", "IBM"), ("some other version", "IBM")) - val TESTSOFT: Seq[(String, String)] = - Seq(("IBM", "3.3.3-a"), ("IBM", "some other version")) + val TESTCERT: Seq[(String, String, Option[Long])] = + Seq(("1.1.1", "IBM", Option(1L)), + ("some other version", "IBM", Option(0L))) + val TESTCONFIG: Seq[(String, String, Option[Long])] = + Seq(("second", "IBM", None), + ("some other version", "IBM", Option(1L))) + val TESTSOFT: Seq[(String, String, Option[Long])] = + Seq(("IBM", "3.3.3-a", Option(10L)), + ("IBM", "some other version", Option(4L))) val TESTCHG: Seq[(java.sql.Timestamp, String)] = Seq((ApiTime.nowTimestamp, "IBM")) diff --git a/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestPutAgentConfigMgmt.scala b/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestPutAgentConfigMgmt.scala index 3a8c557b..e4cfdc57 100644 --- a/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestPutAgentConfigMgmt.scala +++ b/src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestPutAgentConfigMgmt.scala @@ -1,8 +1,8 @@ package com.horizon.exchangeapi.route.agentconfigurationmanagement -import com.horizon.exchangeapi.tables.{AgbotMsgRow, AgbotMsgsTQ, AgbotRow, AgbotsTQ, AgentCertificateVersionsTQ, AgentConfigurationVersionsTQ, AgentSoftwareVersionsTQ, AgentVersionsChangedTQ, AgentVersionsRequest, AgentVersionsResponse, NodeRow, NodesTQ, OrgRow, OrgsTQ, ResChangeCategory, ResChangeOperation, ResourceChangeRow, ResourceChangesTQ, SearchOffsetPolicyAttributes, SearchOffsetPolicyTQ, UserRow, UsersTQ} -import com.horizon.exchangeapi.{ApiTime, ApiUtils, GetAgbotMsgsResponse, HttpCode, PostPutUsersRequest, PutAgbotsRequest, Role, TestDBConnection} -import org.json4s.DefaultFormats +import com.horizon.exchangeapi.tables.{AgbotMsgRow, AgbotMsgsTQ, AgbotRow, AgbotsTQ, AgentCertificateVersionsTQ, AgentConfigurationVersionsTQ, AgentSoftwareVersionsTQ, AgentVersionsChangedTQ, AgentVersionsRequest, AgentVersionsResponse, NodeRow, NodesTQ, OrgRow, OrgsTQ, ResChangeCategory, ResChangeOperation, ResChangeResource, ResourceChangeRow, ResourceChangesTQ, SearchOffsetPolicyAttributes, SearchOffsetPolicyTQ, UserRow, UsersTQ} +import com.horizon.exchangeapi.{ApiTime, ApiUtils, ChangeEntry, GetAgbotMsgsResponse, HttpCode, PostPutUsersRequest, PutAgbotsRequest, ResourceChangesRequest, ResourceChangesRespObject, Role, TestDBConnection} +import org.json4s.{DefaultFormats, Formats} import org.json4s.jackson.JsonMethods.parse import org.json4s.native.Serialization.write import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, DoNotDiscover, Suite} @@ -15,15 +15,15 @@ import scala.concurrent.duration.{Duration, DurationInt} @DoNotDiscover class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with BeforeAndAfterEach with Suite { - private val ACCEPT = ("Accept", "application/json") - private val CONTENT = ("Content-Type", "application/json") + private val ACCEPT: (String, String) = ("Accept", "application/json") + private val CONTENT: (String, String) = ("Content-Type", "application/json") private val AWAITDURATION: Duration = 15.seconds private val DBCONNECTION: TestDBConnection = new TestDBConnection // private val ORGID = "TestPutAgentConfigMgmt" - private val ROOTAUTH = ("Authorization", "Basic " + ApiUtils.encode(Role.superUser + ":" + sys.env.getOrElse("EXCHANGE_ROOTPW", ""))) - private val URL = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" + private val ROOTAUTH: (String, String) = ("Authorization", "Basic " + ApiUtils.encode(Role.superUser + ":" + sys.env.getOrElse("EXCHANGE_ROOTPW", ""))) + private val URL: String = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" - private implicit val formats = DefaultFormats + private implicit val formats: Formats = DefaultFormats.withLong private val TESTAGBOT: AgbotRow = AgbotRow(id = "TestPutAgentConfigMgmt/a1", @@ -69,7 +69,9 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef } override def afterAll(): Unit = { - Await.ready(DBCONNECTION.getDb.run(ResourceChangesTQ.filter(_.orgId startsWith "TestPutAgentConfigMgmt").delete andThen + Await.ready(DBCONNECTION.getDb.run(ResourceChangesTQ.filter(change => {(change.orgId startsWith "TestPutAgentConfigMgmt") || + (change.orgId === "IBM" && + change.resource === "agentfileversion")}).delete andThen OrgsTQ.filter(_.orgid startsWith "TestPutAgentConfigMgmt").delete), AWAITDURATION) DBCONNECTION.getDb.close() @@ -94,7 +96,7 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef } // Agent Certificate Versions that are dynamically needed, specific to the test case. - def fixtureAgentCertVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentCertVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try { Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -104,7 +106,7 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef } // Agent Configuration Versions that are dynamically needed, specific to the test case. - def fixtureAgentConfigVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentConfigVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try { Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -114,7 +116,7 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef } // Agent Software Versions that are dynamically needed, specific to the test case. - def fixtureAgentSoftVersions(testCode: Seq[(String, String)] => Any, testData: Seq[(String, String)]): Any = { + def fixtureAgentSoftVersions(testCode: Seq[(String, String, Option[Long])] => Any, testData: Seq[(String, String, Option[Long])]): Any = { try { Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ ++= testData), AWAITDURATION) testCode(testData) @@ -213,9 +215,9 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef } test("PUT /v1/orgs/IBM/AgentFileVersion -- 201 Created - Root") { - val TESTCERT: Seq[String] = Seq("1.1.1") - val TESTCONFIG: Seq[String] = Seq("2.2.2") - val TESTSOFT: Seq[String] = Seq("3.4.3") + val TESTCERT: Seq[String] = Seq("1.1.1.1", "1.1.1") + val TESTCONFIG: Seq[String] = Seq("2.2.2.2", "2.2.2") + val TESTSOFT: Seq[String] = Seq("3.3.3.3", "3.3.3") val requestBody: AgentVersionsRequest = AgentVersionsRequest(agentCertVersions = TESTCERT, @@ -230,9 +232,9 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef val versions: AgentVersionsRequest = parse(request.body).extract[AgentVersionsRequest] - val certificates: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) + val certificates: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.sortBy(_.priority.asc.nullsLast).result), AWAITDURATION) val changed: Seq[(java.sql.Timestamp, String)] = Await.result(DBCONNECTION.getDb.run(AgentVersionsChangedTQ.result), AWAITDURATION) - val configurations: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) + val configurations: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.sortBy(_.priority.asc.nullsLast).result), AWAITDURATION) val resource: Seq[ResourceChangeRow] = Await.result( DBCONNECTION.getDb.run( @@ -240,32 +242,57 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef .filter(_.id === "IBM") .filter(_.operation === ResChangeOperation.MODIFIED.toString) .filter(_.orgId === "IBM") - .filter(_.resource === ResChangeCategory.ORG.toString) + .filter(_.resource === ResChangeResource.AGENTFILEVERSION.toString) .sortBy(_.changeId.desc) .result ), AWAITDURATION) - val software: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) + val software: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.sortBy(_.priority.asc.nullsLast).result), AWAITDURATION) - assert(certificates.length === 1) + assert(certificates.length === 2) assert(changed.length === 1) - assert(configurations.length === 1) + assert(configurations.length === 2) assert(resource.nonEmpty) - assert(software.length === 1) + assert(software.length === 2) assert(certificates.head._1 === TESTCERT(0)) assert(certificates.head._2 === "IBM") + assert(certificates.head._3.getOrElse(-1) === 0) + assert(certificates(1)._1 === TESTCERT(1)) + assert(certificates(1)._2 === "IBM") + assert(certificates(1)._3.getOrElse(-1) === 1) assert(configurations.head._1 === TESTCONFIG(0)) assert(configurations.head._2 === "IBM") + assert(configurations.head._3.getOrElse(-1) === 0) + assert(configurations(1)._1 === TESTCONFIG(1)) + assert(configurations(1)._2 === "IBM") + assert(configurations(1)._3.getOrElse(-1) === 1) assert(software.head._1 === "IBM") assert(software.head._2 === TESTSOFT(0)) + assert(certificates.head._3.getOrElse(-1) === 0) + assert(software(1)._1 === "IBM") + assert(software(1)._2 === TESTSOFT(1)) + assert(software(1)._3.getOrElse(-1) === 1) + + val request2: HttpResponse[String] = Http(URL + "TestPutAgentConfigMgmt/changes").postData(write(ResourceChangesRequest(changeId = 0, lastUpdated = None, maxRecords = 1000, orgList = None))).method("post").headers(CONTENT).headers(ACCEPT).headers(("Authorization","Basic " + ApiUtils.encode("TestPutAgentConfigMgmt/u1:u1pw"))).asString + info("code: " + request2.code) + info("body: " + request2.body) + + assert(request2.code === HttpCode.POST_OK.intValue) + + val versionChanges: List[ChangeEntry] = parse(request2.body).extract[ResourceChangesRespObject].changes.filter(change => {change.orgId === "IBM" && change.resource === "agentfileversion"}) + + assert(versionChanges.head.id === "IBM") + assert(versionChanges.head.operation === "modified") + assert(versionChanges.head.orgId === "IBM") + assert(versionChanges.head.resource === "agentfileversion") } test("PUT /v1/orgs/IBM/AgentFileVersion -- 201 Updated - Root") { - Await.ready(DBCONNECTION.getDb.run((AgentCertificateVersionsTQ += ("1.0.0", "IBM")) andThen - (AgentConfigurationVersionsTQ += ("1.0.1", "IBM")) andThen - (AgentSoftwareVersionsTQ += ("IBM", "1.1.0")) andThen + Await.ready(DBCONNECTION.getDb.run((AgentCertificateVersionsTQ += ("1.0.0", "IBM", None)) andThen + (AgentConfigurationVersionsTQ += ("1.0.1", "IBM", None)) andThen + (AgentSoftwareVersionsTQ += ("IBM", "1.1.0", None)) andThen (AgentVersionsChangedTQ += (ApiTime.nowUTCTimestamp, "IBM"))), AWAITDURATION) val TESTCERT: Seq[String] = Seq("3.1.1") @@ -283,9 +310,9 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef assert(request.code === HttpCode.PUT_OK.intValue) - val certificates: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) + val certificates: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) val changed: Seq[(java.sql.Timestamp, String)] = Await.result(DBCONNECTION.getDb.run(AgentVersionsChangedTQ.result), AWAITDURATION) - val configurations: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) + val configurations: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) val resource: Seq[ResourceChangeRow] = Await.result( DBCONNECTION.getDb.run( @@ -293,11 +320,11 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef .filter(_.id === "IBM") .filter(_.operation === ResChangeOperation.MODIFIED.toString) .filter(_.orgId === "IBM") - .filter(_.resource === ResChangeCategory.ORG.toString) + .filter(_.resource === ResChangeResource.AGENTFILEVERSION.toString) .sortBy(_.changeId.desc) .result ), AWAITDURATION) - val software: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) + val software: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) assert(certificates.length === 1) assert(changed.length === 1) @@ -344,10 +371,10 @@ class TestPutAgentConfigMgmt extends AnyFunSuite with BeforeAndAfterAll with Bef val versions: AgentVersionsRequest = parse(request.body).extract[AgentVersionsRequest] - val certificates: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) + val certificates: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentCertificateVersionsTQ.result), AWAITDURATION) val changed: Seq[(java.sql.Timestamp, String)] = Await.result(DBCONNECTION.getDb.run(AgentVersionsChangedTQ.result), AWAITDURATION) - val configurations: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) - val software: Seq[(String, String)] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) + val configurations: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentConfigurationVersionsTQ.result), AWAITDURATION) + val software: Seq[(String, String, Option[Long])] = Await.result(DBCONNECTION.getDb.run(AgentSoftwareVersionsTQ.result), AWAITDURATION) assert(certificates.length === 1) assert(changed.length === 1)