diff --git a/.gitignore b/.gitignore index dc1e52b6..7af8e053 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Created/used by sbt /bin/ project/project/ +project/target/ target/ .lib/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 56a4b9a2..65c51856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [2.126.0](https://github.com/open-horizon/exchange-api/pull/728) - 2024-11-04 +- Issue 201: The number of registered and unregistered Nodes has been added as reported metrics. +- Issue 624: Http 404 is returned when referencing an non-existent Organization. +- Rebuilt ../admin/status +- Rebuilt ../admin/orgstatus +- Rebuilt ../org/{org}/status +- Fixed some issues with generated swagger document +- Minor dependency cleanup. +- Minor whitespace adjustment +- SBT 1.10.1 -> 1.10.5 + + ## [2.125.3](https://github.com/open-horizon/exchange-api/pull/730) - 2024-11-04 - issue 594: Rework unit-test for ApiUtilsSuite. - Split ApiUtilsSuite in two tests: TestApiUtilsTime and TestNodeAgbotTokenValidation. diff --git a/Makefile b/Makefile index 90313625..41f1affe 100644 --- a/Makefile +++ b/Makefile @@ -314,6 +314,7 @@ docker-push-version-only: test: : $${EXCHANGE_ROOT_PW:?} # this verifies these env vars are set sbt test + sbt onlyAdminStatusTests # Cleanup -------------------------------------------------------------target/docker/.docker-run-icp-------- diff --git a/build.sbt b/build.sbt index cc969194..a81ca1d6 100644 --- a/build.sbt +++ b/build.sbt @@ -2,11 +2,14 @@ // This plugin is for building the docker image of our exchange svr import scala.io.Source -import scala.sys.process._ -import com.typesafe.sbt.packager.docker._ +import scala.sys.process.* +import com.typesafe.sbt.packager.docker.* enablePlugins(JavaAppPackaging, DockerPlugin) +// Maintains test suite isolation for GET .../v1/admin/status testcases. +addCommandAlias("onlyAdminStatusTests", """set root / Test / testOptions -= Tests.Argument("-l", "org.openhorizon.exchangeapi.tag.AdminStatusTest"); testOnly org.openhorizon.exchangeapi.route.administration.TestGetAdminStatus -- -n org.openhorizon.exchangeapi.tag.AdminStatusTest""".stripMargin) + // For latest versions, see https://mvnrepository.com/ lazy val pekkoHttpVersion = settingKey[String]("Version of Pekko-Http") lazy val pekkoVersion = settingKey[String]("Version of Pekko") @@ -49,8 +52,6 @@ lazy val root = (project in file(".")) libraryDependencies ++= Seq( "org.apache.pekko" %% "pekko-http" % pekkoHttpVersion.value, "org.apache.pekko" %% "pekko-http-xml" % pekkoHttpVersion.value, - // "org.apache.pekko" %% "pekko-stream" % "[2.6.14,)", - // "org.apache.pekko" %% "pekko-http-spray-json" % "[10.2.1,)", "com.github.pjfanning" %% "pekko-http-jackson" % "[3.0.0,)", "org.apache.pekko" %% "pekko-http-cors" % "[1.1.0]", "org.apache.pekko" %% "pekko-slf4j" % "[1.1.1]", @@ -59,23 +60,12 @@ lazy val root = (project in file(".")) "org.json4s" %% "json4s-jackson" % "4.0.6", "jakarta.ws.rs" % "jakarta.ws.rs-api" % "[3.1.0]", - // "org.glassfish.jersey.core" % "jersey-common" % "1.2.1", // Required at runtime by javax.ws.rs-api "com.github.swagger-akka-http" %% "swagger-pekko-http" % "[2.14.0]", - "com.github.swagger-akka-http" %% "swagger-scala-module" % "[2.12.0,)", - //"io.swagger.core.v3" % "swagger-core-jakarta" % "[2.1.12]", // Version 2.1.13+ requires newer versions of slick and slick-hikaricp - //"io.swagger.core.v3" % "swagger-jaxrs2-jakarta" % "[2.1.12]", // Version 2.1.13+ requires newer versions of slick and slick-hikaricp "ch.qos.logback" % "logback-classic" % "1.5.6", - //"net.logstash.logback" % "logstash-logback-encoder" % "[7.4,)", - // "com.typesafe.slick" %% "slick" % "[3.3.3]", // Version 3.4.1 depends on slick-pg and slick-pg_json4s v0.21.0 "com.typesafe.slick" %% "slick-hikaricp" % "[3.4.1]", // Version 3.4.1 depends on slick-pg and slick-pg_json4s v0.21.0 - // "com.github.tminglei" %% "slick-pg" % "[0.20.4]", // Version 0.21.0 depends on version 3.4.0 of slick and slick-hikaricp "com.github.tminglei" %% "slick-pg_json4s" % "[0.21.0]", // Version 0.21.0 depends on version 3.4.0 of slick and slick-hikaricp "org.postgresql" % "postgresql" % "[42.7.1,)", - // "com.zaxxer" % "HikariCP" % "[3.4.5,)", - // "org.slf4j" % "slf4j-simple" % "[1.7.25]", // Version 1.7.35+ requires newer versions of slick and slick-hikaricp - // "ch.qos.logback" % "logback-classic" % "1.3.0-alpha5", - //"com.mchange" % "c3p0" % "[0.9.5.5,)", "org.scalaj" %% "scalaj-http" % "[2.4.2]", // Deprecated as of April 2022, in v2.4.2 "com.typesafe" % "config" % "[1.4.3,)", "org.mindrot" % "jbcrypt" % "[0.4,)", // Last version (v0.4) release February 13, 2017 @@ -97,6 +87,7 @@ lazy val root = (project in file(".")) //javaOptions ++= Seq("-Dconfig.file=/home/naphelps/git/exchange-api/target/config.json"), fork := true, Test / javaOptions ++= Seq("--add-opens", "java.base/java.net=ALL-UNNAMED"), + Test / testOptions += Tests.Argument("-l", "org.openhorizon.exchangeapi.tag.AdminStatusTest"), // No test suite isolation. // Used when running test suites with HTTPS. // Requires path to your PKCS #12 cryptographic store and its password. // fork := true, diff --git a/docs/openapi-3-developer.json b/docs/openapi-3-developer.json index 0b97b0dc..fd899e78 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.125.2" + "version" : "2.126.0" }, "externalDocs" : { "description" : "Open-horizon ExchangeAPI", @@ -124,7 +124,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/GetAdminOrgStatusResponse" + "$ref" : "#/components/schemas/AdminOrgStatus" } } } @@ -169,14 +169,14 @@ "tags" : [ "administration" ], "summary" : "Returns status of the Exchange server", "description" : "Returns a dictionary of statuses/statistics. Can be run by any user.", - "operationId" : "getStatus", + "operationId" : "adminStatus", "responses" : { "200" : { "description" : "response body", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/GetAdminStatusResponse" + "$ref" : "#/components/schemas/AdminStatus" } } } @@ -190,6 +190,44 @@ } } }, + "/v1/orgs/{organization}/status" : { + "get" : { + "tags" : [ "organization" ], + "summary" : "Returns summary status of the org", + "description" : "Returns the totals of key resources in the org. Can be run by any id in this org or a hub admin.", + "operationId" : "orgStatus", + "parameters" : [ { + "name" : "organization", + "in" : "path", + "description" : "Organization id.", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "response body", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminStatus" + } + } + } + }, + "401" : { + "description" : "invalid credentials" + }, + "403" : { + "description" : "access denied" + }, + "404" : { + "description" : "not found" + } + } + } + }, "/v1/admin/version" : { "get" : { "tags" : [ "administration" ], @@ -251,15 +289,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -359,15 +388,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -469,15 +489,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -618,15 +629,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -673,15 +675,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -804,15 +797,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -929,15 +913,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -997,15 +972,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -1068,15 +1034,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -1116,15 +1073,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "201" : { "description" : "response body", @@ -1179,15 +1127,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -1243,15 +1182,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -1298,15 +1228,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -1540,15 +1461,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -1690,15 +1602,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -1949,15 +1852,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -2311,15 +2205,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -2520,15 +2405,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -2773,15 +2649,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -2903,15 +2770,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -2951,15 +2809,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -3007,15 +2856,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -3075,15 +2915,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -3172,15 +3003,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -3485,15 +3307,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -3748,15 +3561,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -3835,15 +3639,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -4056,15 +3851,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -4193,15 +3979,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -4338,15 +4115,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -4398,7 +4166,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/PutNodeErrorRequest" + "$ref" : "#/components/schemas/List" }, "example" : { "errors" : [ { @@ -4456,15 +4224,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -4504,15 +4263,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "201" : { "description" : "response body", @@ -4566,15 +4316,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -4790,15 +4531,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -4963,15 +4695,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5082,15 +4805,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5225,15 +4939,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -5273,15 +4978,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5420,15 +5116,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -5476,15 +5163,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5639,15 +5317,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -5687,15 +5356,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5761,15 +5421,211 @@ "type" : "string" } } ], + "responses" : { + "204" : { + "description" : "deleted" + }, + "401" : { + "description" : "invalid credentials" + }, + "403" : { + "description" : "access denied" + }, + "404" : { + "description" : "not found" + } + } + } + }, + "/v1/orgs/{organization}/nodes/{node}/managementStatus/{mgmtpolicy}" : { + "get" : { + "tags" : [ "node/management policy" ], + "summary" : "Returns status for nodeid", + "description" : "Returns the management status of the node (edge device) with the specified id. Can be run by that node, a user, or an agbot.", + "operationId" : "getStatusMangementPolicy", + "parameters" : [ { + "name" : "organization", + "in" : "path", + "description" : "Organization id.", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "node", + "in" : "path", + "description" : "ID of the node.", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "attribute", + "in" : "query", + "description" : "Which attribute value should be returned. Only 1 attribute can be specified, and it must be 1 of the direct attributes of the node resource (not of the services). If not specified, the entire node resource (including services) will be returned", + "schema" : { + "type" : "string" + } + }, { + "name" : "mgmtpolicy", + "in" : "path", + "description" : "ID of the node management policy.", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "response body", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetNMPStatusResponse" + }, + "example" : { + "managementStatus" : { + "mgmtpolicy" : { + "agentUpgradePolicyStatus" : { + "scheduledTime" : "", + "startTime" : "", + "endTime" : "", + "upgradedVersions" : { + "softwareVersion" : "1.1.1", + "certVersion" : "2.2.2", + "configVersion" : "3.3.3" + }, + "status" : "success|failed|in progress", + "errorMessage" : "Upgrade process failed", + "lastUpdated" : "" + } + } + }, + "lastIndex" : "0" + } + } + } + }, + "400" : { + "description" : "bad input" + }, + "401" : { + "description" : "invalid credentials" + }, + "403" : { + "description" : "access denied" + }, + "404" : { + "description" : "not found" + } + } + }, + "put" : { + "tags" : [ "node/management policy" ], + "summary" : "Adds/updates the status of the Management Policy running on the Node.", + "description" : "Adds or updates the run time status of a Management Policy running on a Node. This is called by the Agreement Bot.", + "operationId" : "putStatusManagementPolicy", + "parameters" : [ { + "name" : "organization", + "in" : "path", + "description" : "Organization identifier", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "node", + "in" : "path", + "description" : "Node identifier", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "mgmtpolicy", + "in" : "path", + "description" : "Management Policy identifier", + "required" : true, + "schema" : { + "type" : "string" + } + } ], "requestBody" : { "content" : { - "*/*" : { + "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/PutNodeMgmtPolStatusRequest" + }, + "example" : { + "agentUpgradePolicyStatus" : { + "scheduledTime" : "", + "startTime" : "", + "endTime" : "", + "upgradedVersions" : { + "softwareVersion" : "1.1.1", + "certVersion" : "2.2.2", + "configVersion" : "3.3.3" + }, + "status" : "success|failed|in progress", + "errorMessage" : "Upgrade process failed" + } } } - } + }, + "required" : true }, + "responses" : { + "201" : { + "description" : "response body", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiResponse" + } + } + } + }, + "401" : { + "description" : "invalid credentials" + }, + "403" : { + "description" : "access denied" + }, + "404" : { + "description" : "not found" + } + } + }, + "delete" : { + "tags" : [ "node/management policy" ], + "summary" : "Deletes the status of the Management Policy running on the Node", + "description" : "Deletes the run time status of a Management Policy running on a Node. This is called by the Agreement Bot.", + "operationId" : "deleteStatusMangementPolicy", + "parameters" : [ { + "name" : "organization", + "in" : "path", + "description" : "Organization identifier.", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "node", + "in" : "path", + "description" : "Node identifier", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "mgmtpolicy", + "in" : "path", + "description" : "Management Policy identifier", + "required" : true, + "schema" : { + "type" : "string" + } + } ], "responses" : { "204" : { "description" : "deleted" @@ -5816,15 +5672,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5917,15 +5764,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -5981,15 +5819,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -6036,15 +5865,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -6152,15 +5972,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -6340,15 +6151,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -6367,6 +6169,7 @@ }, "/v1/orgs/{org}/hagroups" : { "get" : { + "tags" : [ "high availability node group" ], "summary" : "Lists all members of all Node Groups (HA Groups)", "description" : "Returns all Node Groups in this org along with the member nodes that the caller has permission to view.", "operationId" : "getNodeGroups", @@ -6379,15 +6182,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -6453,15 +6247,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "201" : { "description" : "resource created - response body:", @@ -6520,15 +6305,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -6723,15 +6499,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -6925,15 +6692,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -7023,115 +6781,59 @@ "description" : "Returns some or all org definitions. Can be run by any user if filter orgType=IBM is used, otherwise can only be run by the root user or a hub admin.", "operationId" : "getOrganizations", "parameters" : [ { - "name" : "orgtype", - "in" : "query", - "description" : "Filter results to only include orgs with this org type. Currently the only supported org type for this route is 'IBM'.", - "content" : { - "application/json" : { - "schema" : { - "type" : "string", - "enum" : [ "IBM" ] - } - } - } - }, { - "name" : "label", - "in" : "query", - "description" : "Filter results to only include orgs with this label (can include % for wildcard - the URL encoding for % is %25)", - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, - "responses" : { - "200" : { - "description" : "response body", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/GetOrgsResponse" - }, - "example" : { - "orgs" : { - "string" : { - "orgType" : "", - "label" : "", - "description" : "", - "lastUpdated" : "", - "tags" : null, - "limits" : { - "maxNodes" : 0 - }, - "heartbeatIntervals" : { - "minInterval" : 0, - "maxInterval" : 0, - "intervalAdjustment" : 0 - } - } - }, - "lastIndex" : 0 - } - } - } - }, - "400" : { - "description" : "bad input" - }, - "401" : { - "description" : "invalid credentials" - }, - "403" : { - "description" : "access denied" - }, - "404" : { - "description" : "not found" - } - } - } - }, - "/v1/orgs/{organization}/status" : { - "get" : { - "tags" : [ "organization" ], - "summary" : "Returns summary status of the org", - "description" : "Returns the totals of key resources in the org. Can be run by any id in this org or a hub admin.", - "operationId" : "getStatus_1", - "parameters" : [ { - "name" : "organization", - "in" : "path", - "description" : "Organization id.", - "required" : true, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { + "name" : "orgtype", + "in" : "query", + "description" : "Filter results to only include orgs with this org type. Currently the only supported org type for this route is 'IBM'.", "content" : { - "*/*" : { + "application/json" : { "schema" : { - "$ref" : "#/components/schemas/Identity" + "type" : "string", + "enum" : [ "IBM" ] } } } - }, + }, { + "name" : "label", + "in" : "query", + "description" : "Filter results to only include orgs with this label (can include % for wildcard - the URL encoding for % is %25)", + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "description" : "response body", "content" : { - "*/*" : { + "application/json" : { "schema" : { - "$ref" : "#/components/schemas/GetOrgStatusResponse" + "$ref" : "#/components/schemas/GetOrgsResponse" + }, + "example" : { + "orgs" : { + "string" : { + "orgType" : "", + "label" : "", + "description" : "", + "lastUpdated" : "", + "tags" : null, + "limits" : { + "maxNodes" : 0 + }, + "heartbeatIntervals" : { + "minInterval" : 0, + "maxInterval" : 0, + "intervalAdjustment" : 0 + } + } + }, + "lastIndex" : 0 } } } }, + "400" : { + "description" : "bad input" + }, "401" : { "description" : "invalid credentials" }, @@ -7159,15 +6861,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "201" : { "description" : "response body:", @@ -7206,15 +6899,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body:", @@ -7389,15 +7073,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -7509,15 +7184,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -7564,15 +7230,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -7778,15 +7435,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -7954,15 +7602,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -8171,15 +7810,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -8298,15 +7928,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -8346,15 +7967,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -8468,15 +8080,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -8524,15 +8127,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -8654,15 +8248,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -8702,15 +8287,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -8759,15 +8335,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -8869,15 +8436,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "201" : { "description" : "post ok" @@ -8917,15 +8475,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -9107,15 +8656,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "type" : "string" - } - } - } - }, "responses" : { "204" : { "description" : "deleted" @@ -9210,15 +8750,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/Identity" - } - } - } - }, "responses" : { "200" : { "description" : "response body", @@ -9324,17 +8855,6 @@ } } }, - "OptionString" : { - "type" : "object", - "properties" : { - "empty" : { - "type" : "boolean" - }, - "defined" : { - "type" : "boolean" - } - } - }, "Node" : { "required" : [ "arch", "heartbeatIntervals", "lastHeartbeat", "lastUpdated", "msgEndPoint", "name", "nodeType", "owner", "pattern", "publicKey", "registeredServices", "softwareVersions", "token", "userInput" ], "type" : "object", @@ -9403,6 +8923,30 @@ } } }, + "NodeMangementPolicyStatus" : { + "required" : [ "scheduledTime" ], + "type" : "object", + "properties" : { + "scheduledTime" : { + "type" : "string" + }, + "startTime" : { + "type" : "string" + }, + "endTime" : { + "type" : "string" + }, + "upgradedVersions" : { + "$ref" : "#/components/schemas/UpgradedVersions" + }, + "status" : { + "type" : "string" + }, + "errorMessage" : { + "type" : "string" + } + } + }, "MaxChangeIdResponse" : { "required" : [ "maxChangeId" ], "type" : "object", @@ -9460,7 +9004,7 @@ } }, "constraints" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -9509,7 +9053,7 @@ "type" : "string" }, "patterns" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -9529,7 +9073,7 @@ } }, "constraints" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -9545,50 +9089,24 @@ "type" : "boolean" }, "label" : { - "type" : "string" - } - } - }, - "PostServiceSearchRequest" : { - "required" : [ "orgid", "serviceArch", "serviceURL", "serviceVersion" ], - "type" : "object", - "properties" : { - "serviceVersion" : { - "type" : "string" - }, - "serviceURL" : { - "type" : "string" - }, - "serviceArch" : { - "type" : "string" - }, - "orgid" : { - "type" : "string" - } - } - }, - "Identity" : { - "type" : "object", - "properties" : { - "superUser" : { - "type" : "boolean" - }, - "admin" : { - "type" : "boolean" - }, - "hubAdmin" : { - "type" : "boolean" - }, - "anonymous" : { - "type" : "boolean" + "type" : "string" + } + } + }, + "PostServiceSearchRequest" : { + "required" : [ "orgid", "serviceArch", "serviceURL", "serviceVersion" ], + "type" : "object", + "properties" : { + "serviceVersion" : { + "type" : "string" }, - "multiTenantAgbot" : { - "type" : "boolean" + "serviceURL" : { + "type" : "string" }, - "org" : { + "serviceArch" : { "type" : "string" }, - "identity" : { + "orgid" : { "type" : "string" } } @@ -9627,21 +9145,6 @@ } } }, - "PutNodeErrorRequest" : { - "required" : [ "errors", "jsonFormats" ], - "type" : "object", - "properties" : { - "errors" : { - "type" : "array", - "items" : { - "type" : "object" - } - }, - "jsonFormats" : { - "$ref" : "#/components/schemas/Formats" - } - } - }, "GetNMPStatusResponse" : { "required" : [ "lastIndex", "managementStatus" ], "type" : "object", @@ -9675,7 +9178,7 @@ } }, "constraints" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -9709,6 +9212,18 @@ } } }, + "AdminOrgStatus" : { + "type" : "object", + "properties" : { + "msg" : { + "type" : "string", + "default" : "" + }, + "nodes" : { + "type" : "object" + } + } + }, "PServiceVersions" : { "required" : [ "version" ], "type" : "object", @@ -9736,39 +9251,6 @@ } } }, - "GetOrgStatusResponse" : { - "required" : [ "SchemaVersion", "msg", "numberOfNodeAgreements", "numberOfNodeMsgs", "numberOfNodes", "numberOfRegisteredNodes", "numberOfUsers" ], - "type" : "object", - "properties" : { - "msg" : { - "type" : "string" - }, - "numberOfUsers" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfNodes" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfNodeAgreements" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfRegisteredNodes" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfNodeMsgs" : { - "type" : "integer", - "format" : "int32" - }, - "SchemaVersion" : { - "type" : "integer", - "format" : "int32" - } - } - }, "PostNodeConfigStateRequest" : { "required" : [ "configState", "jsonFormats", "org", "url" ], "type" : "object", @@ -9895,6 +9377,7 @@ } }, "ResourceChangesRequest" : { + "required" : [ "changeId", "maxRecords" ], "type" : "object", "properties" : { "changeId" : { @@ -9913,9 +9396,6 @@ "items" : { "type" : "string" } - }, - "anyProblem" : { - "type" : "string" } } }, @@ -10167,6 +9647,73 @@ } } }, + "AdminStatus" : { + "type" : "object", + "properties" : { + "dbSchemaVersion" : { + "type" : "integer", + "format" : "int32", + "default" : -1 + }, + "msg" : { + "type" : "string", + "default" : "" + }, + "numberOfAgbotAgreements" : { + "type" : "integer", + "format" : "int32", + "default" : 0 + }, + "numberOfAgbotMsgs" : { + "type" : "integer", + "format" : "int32", + "default" : 0 + }, + "numberOfAgbots" : { + "type" : "integer", + "format" : "int32", + "default" : 0 + }, + "numberOfNodeAgreements" : { + "type" : "integer", + "format" : "int32", + "default" : -1 + }, + "numberOfNodeMsgs" : { + "type" : "integer", + "format" : "int32", + "default" : -1 + }, + "numberOfNodes" : { + "type" : "integer", + "format" : "int32", + "default" : -1 + }, + "numberOfOrganizations" : { + "type" : "integer", + "format" : "int32", + "default" : 0 + }, + "numberOfRegisteredNodes" : { + "type" : "integer", + "format" : "int32" + }, + "numberOfUnregisteredNodes" : { + "type" : "integer", + "format" : "int32" + }, + "numberOfUsers" : { + "type" : "integer", + "format" : "int32", + "default" : -1 + }, + "SchemaVersion" : { + "type" : "integer", + "format" : "int32", + "default" : -1 + } + } + }, "NAgrService" : { "required" : [ "orgid", "pattern", "url" ], "type" : "object", @@ -10243,7 +9790,7 @@ "type" : "boolean" }, "agreementProtocols" : { - "type" : "string", + "type" : "array", "items" : { "type" : "object", "additionalProperties" : { @@ -10333,7 +9880,7 @@ "$ref" : "#/components/schemas/Formats" }, "userInput" : { - "type" : "string", + "type" : "array", "items" : { "type" : "object", "additionalProperties" : { @@ -10365,7 +9912,7 @@ "type" : "string" }, "nodeOrgids" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -10405,7 +9952,7 @@ "type" : "object", "properties" : { "members" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -10490,35 +10037,6 @@ } } }, - "PatchOrgRequest" : { - "type" : "object", - "properties" : { - "orgType" : { - "type" : "string" - }, - "label" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "tags" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - }, - "limits" : { - "$ref" : "#/components/schemas/OrgLimits" - }, - "heartbeatIntervals" : { - "$ref" : "#/components/schemas/NodeHeartbeatIntervals" - }, - "jsonFormats" : { - "$ref" : "#/components/schemas/Formats" - } - } - }, "ChangePwRequest" : { "required" : [ "newPassword" ], "type" : "object", @@ -10608,6 +10126,15 @@ } } }, + "PutNodeMgmtPolStatusRequest" : { + "required" : [ "agentUpgradePolicyStatus" ], + "type" : "object", + "properties" : { + "agentUpgradePolicyStatus" : { + "$ref" : "#/components/schemas/NodeMangementPolicyStatus" + } + } + }, "ServiceDockAuth" : { "required" : [ "dockAuthId", "lastUpdated", "registry", "token", "username" ], "type" : "object", @@ -10692,7 +10219,8 @@ "properties" : { "changedSince" : { "type" : "integer", - "format" : "int64" + "format" : "int64", + "default" : 0 }, "nodeOrgids" : { "type" : "array", @@ -10701,7 +10229,8 @@ } }, "numEntries" : { - "type" : "object" + "type" : "integer", + "format" : "int32" }, "session" : { "type" : "string" @@ -10858,7 +10387,7 @@ } }, "constraints" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -10980,7 +10509,7 @@ "type" : "string" }, "nodeOrgids" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -11203,7 +10732,7 @@ } }, "constraints" : { - "type" : "string", + "type" : "array", "items" : { "type" : "string" } @@ -11465,53 +10994,6 @@ "message" : { "type" : "string" }, - "suppressed" : { - "type" : "array", - "items" : { - "type" : "object", - "properties" : { - "stackTrace" : { - "type" : "array", - "items" : { - "type" : "object", - "properties" : { - "classLoaderName" : { - "type" : "string" - }, - "moduleName" : { - "type" : "string" - }, - "moduleVersion" : { - "type" : "string" - }, - "methodName" : { - "type" : "string" - }, - "fileName" : { - "type" : "string" - }, - "lineNumber" : { - "type" : "integer", - "format" : "int32" - }, - "nativeMethod" : { - "type" : "boolean" - }, - "className" : { - "type" : "string" - } - } - } - }, - "message" : { - "type" : "string" - }, - "localizedMessage" : { - "type" : "string" - } - } - } - }, "localizedMessage" : { "type" : "string" } @@ -11592,47 +11074,6 @@ } } }, - "GetAdminStatusResponse" : { - "required" : [ "dbSchemaVersion", "msg", "numberOfAgbotAgreements", "numberOfAgbotMsgs", "numberOfAgbots", "numberOfNodeAgreements", "numberOfNodeMsgs", "numberOfNodes", "numberOfUsers" ], - "type" : "object", - "properties" : { - "msg" : { - "type" : "string" - }, - "numberOfUsers" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfNodes" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfNodeAgreements" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfNodeMsgs" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfAgbots" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfAgbotAgreements" : { - "type" : "integer", - "format" : "int32" - }, - "numberOfAgbotMsgs" : { - "type" : "integer", - "format" : "int32" - }, - "dbSchemaVersion" : { - "type" : "integer", - "format" : "int32" - } - } - }, "AgentVersionsResponse" : { "required" : [ "agentCertVersions", "agentConfigVersions", "agentSoftwareVersions", "lastUpdated" ], "type" : "object", @@ -11848,7 +11289,7 @@ }, "token" : { "type" : "string", - "default" : "********" + "default" : "***************" }, "userInput" : { "type" : "array", @@ -11928,21 +11369,6 @@ } } }, - "GetAdminOrgStatusResponse" : { - "required" : [ "msg", "nodes" ], - "type" : "object", - "properties" : { - "msg" : { - "type" : "string" - }, - "nodes" : { - "type" : "object", - "additionalProperties" : { - "type" : "object" - } - } - } - }, "PostPutServiceRequest" : { "required" : [ "arch", "jsonFormats", "label", "public", "sharable", "url", "version" ], "type" : "object", @@ -11984,7 +11410,7 @@ } }, "userInput" : { - "type" : "string", + "type" : "array", "items" : { "type" : "object", "additionalProperties" : { @@ -12016,6 +11442,7 @@ } }, "PostPutOrgRequest" : { + "required" : [ "description", "jsonFormats", "label" ], "type" : "object", "properties" : { "orgType" : { @@ -12125,7 +11552,7 @@ "type" : "boolean" }, "agreementProtocols" : { - "type" : "string", + "type" : "array", "items" : { "type" : "object", "additionalProperties" : { diff --git a/docs/openapi-3-user.json b/docs/openapi-3-user.json index 01b27f4c..7118dfab 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.125.2" + "version" : "2.126.0" }, "externalDocs" : { "description" : "Open-horizon ExchangeAPI", diff --git a/project/build.properties b/project/build.properties index ee4c672c..db1723b0 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version=1.10.5 diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 160889c2..7a671679 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -6,86 +6,86 @@ api { "WRITE_AGENT_CONFIG_MGMT"] Agbot = ["DATA_HEARTBEAT_MY_AGBOTS", - "MAXCHANGEID", - "READ_MYSELF", - "READ_AGENT_CONFIG_MGMT", - "READ_ALL_AGBOTS", - "READ_ALL_NODES", - "READ_ALL_SERVICES", - "READ_ALL_PATTERNS", - "READ_ALL_BUSINESS", - "READ_ALL_MANAGEMENT_POLICY", - "READ_MY_AGBOTS", - "READ_MY_ORG", - "SEND_MSG_TO_NODE", - "WRITE_AGENT_CONFIG_MGMT", - "WRITE_MYSELF"] + "MAXCHANGEID", + "READ_MYSELF", + "READ_AGENT_CONFIG_MGMT", + "READ_ALL_AGBOTS", + "READ_ALL_NODES", + "READ_ALL_SERVICES", + "READ_ALL_PATTERNS", + "READ_ALL_BUSINESS", + "READ_ALL_MANAGEMENT_POLICY", + "READ_MY_AGBOTS", + "READ_MY_ORG", + "SEND_MSG_TO_NODE", + "WRITE_AGENT_CONFIG_MGMT", + "WRITE_MYSELF"] Anonymous = [] # Not actually used HubAdmin = ["CREATE_IN_OTHER_ORGS", - "CREATE_ORGS", - "CREATE_USER", - "DELETE_ORG", - "ORGSTATUS", - "READ_AGENT_CONFIG_MGMT", - "READ_ALL_AGBOTS", - "READ_IBM_ORGS", - "READ_MY_ORG", - "READ_MY_USERS", - "READ_MYSELF", - "READ_OTHER_ORGS", - "SET_IBM_ORG_TYPE", - "STATUS", - "UTILITIES", - "WRITE_AGENT_CONFIG_MGMT", - "WRITE_ALL_AGBOTS", - "WRITE_MY_ORG", - "WRITE_MY_USERS", - "WRITE_MYSELF", - "WRITE_OTHER_ORGS"] + "CREATE_ORGS", + "CREATE_USER", + "DELETE_ORG", + "ORGSTATUS", + "READ_AGENT_CONFIG_MGMT", + "READ_ALL_AGBOTS", + "READ_IBM_ORGS", + "READ_MY_ORG", + "READ_MY_USERS", + "READ_MYSELF", + "READ_OTHER_ORGS", + "SET_IBM_ORG_TYPE", + "STATUS", + "UTILITIES", + "WRITE_AGENT_CONFIG_MGMT", + "WRITE_ALL_AGBOTS", + "WRITE_MY_ORG", + "WRITE_MY_USERS", + "WRITE_MYSELF", + "WRITE_OTHER_ORGS"] Node = ["MAXCHANGEID", - "READ_AGENT_CONFIG_MGMT", - "READ_ALL_AGBOTS", - "READ_ALL_BUSINESS", - "READ_ALL_MANAGEMENT_POLICY", - "READ_ALL_PATTERNS", - "READ_ALL_SERVICES", - "READ_MY_ORG", - "READ_MYSELF", - "SEND_MSG_TO_AGBOT", - "WRITE_MYSELF"], + "READ_AGENT_CONFIG_MGMT", + "READ_ALL_AGBOTS", + "READ_ALL_BUSINESS", + "READ_ALL_MANAGEMENT_POLICY", + "READ_ALL_PATTERNS", + "READ_ALL_SERVICES", + "READ_MY_ORG", + "READ_MYSELF", + "SEND_MSG_TO_AGBOT", + "WRITE_MYSELF"], SuperUser = ["ALL"] User = ["CREATE_AGBOT", - "CREATE_BUSINESS", - "CREATE_NODE", - "CREATE_PATTERNS", - "CREATE_SERVICES", - "DATA_HEARTBEAT_MY_AGBOTS", - "READ_AGENT_CONFIG_MGMT", - "READ_ALL_AGBOTS", - "READ_ALL_BUSINESS", - "READ_ALL_MANAGEMENT_POLICY", - "READ_ALL_PATTERNS", - "READ_ALL_SERVICES", - "READ_IBM_ORGS", - "READ_MY_AGBOTS", - "READ_MY_BUSINESS", - "READ_MY_NODES", - "READ_MY_PATTERNS", - "READ_MY_MANAGEMENT_POLICY", - "READ_MY_ORG", - "READ_MY_SERVICES", - "READ_MYSELF", - "STATUS", - "UTILITIES", - "WRITE_MY_AGBOTS", - "WRITE_MY_BUSINESS", - "WRITE_MY_NODES", - "WRITE_MY_PATTERNS", - "WRITE_MY_SERVICES", - "WRITE_MYSELF"] + "CREATE_BUSINESS", + "CREATE_NODE", + "CREATE_PATTERNS", + "CREATE_SERVICES", + "DATA_HEARTBEAT_MY_AGBOTS", + "READ_AGENT_CONFIG_MGMT", + "READ_ALL_AGBOTS", + "READ_ALL_BUSINESS", + "READ_ALL_MANAGEMENT_POLICY", + "READ_ALL_PATTERNS", + "READ_ALL_SERVICES", + "READ_IBM_ORGS", + "READ_MY_AGBOTS", + "READ_MY_BUSINESS", + "READ_MY_NODES", + "READ_MY_PATTERNS", + "READ_MY_MANAGEMENT_POLICY", + "READ_MY_ORG", + "READ_MY_SERVICES", + "READ_MYSELF", + "STATUS", + "UTILITIES", + "WRITE_MY_AGBOTS", + "WRITE_MY_BUSINESS", + "WRITE_MY_NODES", + "WRITE_MY_PATTERNS", + "WRITE_MY_SERVICES", + "WRITE_MYSELF"] } db.upgradeTimeoutSeconds = 180 @@ -129,8 +129,6 @@ api { resourceChanges.maxRecordsCap = 10000 # Maximum number of records the notification framework route will return resourceChanges.ttl = 14400 # Number of seconds to keep the history records of resource changes (14400 is 4 hours). When agents miss 1 or more heartbeats, they reset querying the /changes route, so they do not need very old entries - - # ie. root.account_id.ibmcloud_id = 012345689 root.account_id = null root.enabled = true # If set to false it will not honor the root credentials diff --git a/src/main/resources/version.txt b/src/main/resources/version.txt index 01a32c8b..20b6d067 100644 --- a/src/main/resources/version.txt +++ b/src/main/resources/version.txt @@ -1 +1 @@ -2.125.3 +2.126.0 diff --git a/src/main/scala/org/openhorizon/exchangeapi/ExchangeApiApp.scala b/src/main/scala/org/openhorizon/exchangeapi/ExchangeApiApp.scala index 34c6b3a7..f13d5674 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/ExchangeApiApp.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/ExchangeApiApp.scala @@ -12,7 +12,7 @@ import scala.concurrent.{Await, ExecutionContext, Future} import scala.util.{Failure, Success} import org.openhorizon.exchangeapi.route.administration.{ClearAuthCache, Configuration, DropDatabase, HashPassword, InitializeDatabase, OrganizationStatus, Reload, Status, Version} import org.openhorizon.exchangeapi.route.agreement.Confirm -import org.openhorizon.exchangeapi.route.agreementbot.{AgbotsRoutes, Agreement, AgreementBot, AgreementBots, Agreements, DeploymentPattern, DeploymentPatterns, DeploymentPolicies, DeploymentPolicy, Heartbeat, Message, Messages} +import org.openhorizon.exchangeapi.route.agreementbot.{Agreement, AgreementBot, AgreementBots, Agreements, DeploymentPattern, DeploymentPatterns, DeploymentPolicies, DeploymentPolicy, Heartbeat, Message, Messages} import org.openhorizon.exchangeapi.table import org.openhorizon.exchangeapi.table.{ExchangeApiTables, ExchangePostgresProfile} import com.typesafe.config.{ConfigFactory, ConfigParseOptions, ConfigSyntax, ConfigValue} @@ -399,7 +399,7 @@ object ExchangeApiApp extends App status ~ statusManagementPolicy ~ statusNode ~ - statusOrganization ~ + //statusOrganization ~ statuses ~ SwaggerDocService.routes ~ swaggerUiRoutes ~ diff --git a/src/main/scala/org/openhorizon/exchangeapi/SwaggerDocService.scala b/src/main/scala/org/openhorizon/exchangeapi/SwaggerDocService.scala index bb02489d..067a489f 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/SwaggerDocService.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/SwaggerDocService.scala @@ -11,7 +11,7 @@ import io.swagger.v3.oas.models.ExternalDocumentation import org.openhorizon.exchangeapi.route.administration.dropdatabase.Token import org.openhorizon.exchangeapi.route.agent.AgentConfigurationManagement import org.openhorizon.exchangeapi.route.agreement.Confirm -import org.openhorizon.exchangeapi.route.agreementbot.{AgbotsRoutes, Agreement, AgreementBot, AgreementBots, Agreements, DeploymentPattern, DeploymentPatterns, DeploymentPolicies, DeploymentPolicy, Heartbeat, Message, Messages} +import org.openhorizon.exchangeapi.route.agreementbot.{Agreement, AgreementBot, AgreementBots, Agreements, DeploymentPattern, DeploymentPatterns, DeploymentPolicies, DeploymentPolicy, Heartbeat, Message, Messages} import org.openhorizon.exchangeapi.route.catalog.{OrganizationDeploymentPatterns, OrganizationServices} import org.openhorizon.exchangeapi.route.deploymentpattern.{DeploymentPatterns, Search} import org.openhorizon.exchangeapi.route.deploymentpolicy.{DeploymentPolicy, DeploymentPolicySearch} @@ -25,7 +25,6 @@ import org.openhorizon.exchangeapi.route.service.dockerauth.{DockerAuth, DockerA import org.openhorizon.exchangeapi.route.service.key.{Key, Keys} import org.openhorizon.exchangeapi.route.service.{Policy, Service, Services} import org.openhorizon.exchangeapi.route.user.{ChangePassword, Confirm, User, Users} -import org.openhorizon.exchangeapi.table.node.OneService import org.openhorizon.exchangeapi.utility.Configuration /*Swagger references: @@ -51,7 +50,6 @@ object SwaggerDocService extends SwaggerHttpService { classOf[org.openhorizon.exchangeapi.route.administration.Version], classOf[org.openhorizon.exchangeapi.route.agent.AgentConfigurationManagement], classOf[org.openhorizon.exchangeapi.route.agreement.Confirm], - classOf[org.openhorizon.exchangeapi.route.agreementbot.AgbotsRoutes], classOf[org.openhorizon.exchangeapi.route.agreementbot.Agreement], classOf[org.openhorizon.exchangeapi.route.agreementbot.AgreementBot], classOf[org.openhorizon.exchangeapi.route.agreementbot.AgreementBots], @@ -89,7 +87,6 @@ object SwaggerDocService extends SwaggerHttpService { classOf[org.openhorizon.exchangeapi.route.node.message.Message], classOf[org.openhorizon.exchangeapi.route.node.message.Messages], classOf[org.openhorizon.exchangeapi.route.node.Node], - classOf[org.openhorizon.exchangeapi.route.node.NodeDetails], classOf[org.openhorizon.exchangeapi.route.node.Nodes], classOf[org.openhorizon.exchangeapi.route.node.Policy], classOf[org.openhorizon.exchangeapi.route.node.Status], @@ -103,7 +100,6 @@ object SwaggerDocService extends SwaggerHttpService { classOf[org.openhorizon.exchangeapi.route.organization.MyOrganizations], classOf[org.openhorizon.exchangeapi.route.organization.Organization], classOf[org.openhorizon.exchangeapi.route.organization.Organizations], - classOf[org.openhorizon.exchangeapi.route.organization.Status], classOf[org.openhorizon.exchangeapi.route.search.NodeError], classOf[org.openhorizon.exchangeapi.route.search.NodeErrors], classOf[org.openhorizon.exchangeapi.route.search.NodeHealth], diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminOrgStatus.scala b/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminOrgStatus.scala index bd1c5533..57a85f7b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminOrgStatus.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminOrgStatus.scala @@ -1,7 +1,8 @@ package org.openhorizon.exchangeapi.route.administration -class AdminOrgStatus(){ - var msg: String = "" - var nodesByOrg : Map[String, Int] = null - def toGetAdminOrgStatusResponse: GetAdminOrgStatusResponse = GetAdminOrgStatusResponse(msg, nodesByOrg) +import io.swagger.v3.oas.annotations.media.Schema + +case class AdminOrgStatus(msg: String = "", + @Schema(implementation = classOf[Map[String, Int]]) nodes: Map[String, Int]){ + } diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminStatus.scala b/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminStatus.scala index c9219306..75dbf770 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminStatus.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/administration/AdminStatus.scala @@ -1,14 +1,34 @@ package org.openhorizon.exchangeapi.route.administration -class AdminStatus() { - var msg: String = "" - var numberOfUsers: Int = 0 - var numberOfNodes: Int = 0 - var numberOfNodeAgreements: Int = 0 - var numberOfNodeMsgs: Int = 0 - var numberOfAgbots: Int = 0 - var numberOfAgbotAgreements: Int = 0 - var numberOfAgbotMsgs: Int = 0 - var dbSchemaVersion: Int = 0 - def toGetAdminStatusResponse: GetAdminStatusResponse = GetAdminStatusResponse(msg, numberOfUsers, numberOfNodes, numberOfNodeAgreements, numberOfNodeMsgs, numberOfAgbots, numberOfAgbotAgreements, numberOfAgbotMsgs, dbSchemaVersion) +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonInclude.Include + +@JsonInclude(Include.NON_ABSENT) // Hides key/value pairs that are None. +case class AdminStatus(dbSchemaVersion: Option[Int] = Option(-1), + msg: String = "", + numberOfAgbotAgreements: Int = 0, + numberOfAgbotMsgs: Int = 0, + numberOfAgbots: Int = 0, + numberOfNodeAgreements: Option[Int] = Option(-1), + numberOfNodeMsgs: Option[Int] = Option(-1), + numberOfNodes: Option[Int] = Option(-1), + numberOfOrganizations: Int = 0, + numberOfRegisteredNodes: Option[Int] = None, + numberOfUnregisteredNodes: Option[Int] = None, + numberOfUsers: Option[Int] = Option(-1), + SchemaVersion: Option[Int] = Option(-1)) { + def this(DBMetrics: (Int, Int, Int, Int, Int, Int, Option[Int], Option[Int], Int, Int, Int), message: String, organization: Boolean) = + this(dbSchemaVersion = if(!organization) Option(DBMetrics._11) else None, + msg = message, + numberOfAgbotAgreements = DBMetrics._1, + numberOfAgbotMsgs = DBMetrics._2, + numberOfAgbots = DBMetrics._3, + numberOfNodeAgreements = Option(DBMetrics._4), + numberOfNodeMsgs = Option(DBMetrics._5), + numberOfNodes = Option(DBMetrics._6), + numberOfOrganizations = DBMetrics._9, + numberOfRegisteredNodes = DBMetrics._7, + numberOfUnregisteredNodes = DBMetrics._8, + numberOfUsers = Option(DBMetrics._10), + SchemaVersion = if(organization) Option(DBMetrics._11) else None) } diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/administration/GetAdminOrgStatusResponse.scala b/src/main/scala/org/openhorizon/exchangeapi/route/administration/GetAdminOrgStatusResponse.scala deleted file mode 100644 index d73b9be5..00000000 --- a/src/main/scala/org/openhorizon/exchangeapi/route/administration/GetAdminOrgStatusResponse.scala +++ /dev/null @@ -1,3 +0,0 @@ -package org.openhorizon.exchangeapi.route.administration - -final case class GetAdminOrgStatusResponse(msg: String, nodes: Map[String, Int]) diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/administration/GetAdminStatusResponse.scala b/src/main/scala/org/openhorizon/exchangeapi/route/administration/GetAdminStatusResponse.scala deleted file mode 100644 index b5f15b62..00000000 --- a/src/main/scala/org/openhorizon/exchangeapi/route/administration/GetAdminStatusResponse.scala +++ /dev/null @@ -1,3 +0,0 @@ -package org.openhorizon.exchangeapi.route.administration - -final case class GetAdminStatusResponse(msg: String, numberOfUsers: Int, numberOfNodes: Int, numberOfNodeAgreements: Int, numberOfNodeMsgs: Int, numberOfAgbots: Int, numberOfAgbotAgreements: Int, numberOfAgbotMsgs: Int, dbSchemaVersion: Int) diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/administration/OrganizationStatus.scala b/src/main/scala/org/openhorizon/exchangeapi/route/administration/OrganizationStatus.scala index c1327bf1..7fb6310c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/administration/OrganizationStatus.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/administration/OrganizationStatus.scala @@ -6,11 +6,12 @@ import org.apache.pekko.http.scaladsl.server.Directives._ import org.apache.pekko.http.scaladsl.server.Route import com.github.pjfanning.pekkohttpjackson.JacksonSupport import io.swagger.v3.oas.annotations.media.{Content, Schema} -import io.swagger.v3.oas.annotations.{Operation, responses} +import io.swagger.v3.oas.annotations.{Operation, Parameter, responses} import jakarta.ws.rs.{GET, Path} -import org.openhorizon.exchangeapi.auth.{Access, AuthenticationSupport, TAction} +import org.openhorizon.exchangeapi.auth.{Access, AuthRoles, AuthenticationSupport, Identity, TAction} import org.openhorizon.exchangeapi.table.node.NodesTQ -import org.openhorizon.exchangeapi.utility.{ExchMsg, HttpCode} +import org.openhorizon.exchangeapi.table.organization.OrgsTQ +import org.openhorizon.exchangeapi.utility.{ApiRespType, ApiResponse, ExchMsg, HttpCode} import slick.jdbc.PostgresProfile.api._ import scala.concurrent.ExecutionContext @@ -29,41 +30,52 @@ trait OrganizationStatus extends JacksonSupport with AuthenticationSupport { // =========== GET /admin/orgstatus =============================== @GET - @Operation(summary = "Returns the org-specific status of the Exchange server", - description = """Returns a dictionary of statuses/statistics. Can be run by superuser, hub admins, and org admins.""", + @Operation(description = """Returns a dictionary of statuses/statistics. Can be run by superuser, hub admins, and org admins.""", responses = Array(new responses.ApiResponse(responseCode = "200", description = "response body", content = Array(new Content(mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAdminOrgStatusResponse])))), + schema = new Schema(implementation = classOf[AdminOrgStatus])))), new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"))) - def getOrganizationStatus: Route = { - logger.debug("Doing GET /admin/orgstatus") + new responses.ApiResponse(responseCode = "403", description = "access denied")), + summary = "Returns the org-specific status of the Exchange server") + def getOrganizationStatus(@Parameter(hidden = true) identity: Identity): Route = { // Hides fields from being included in the swagger doc as request parameters. + logger.debug("Doing GET /admin/status") complete({ - val orgStatusResp = new AdminOrgStatus() - // perf: use a DBIO.sequence instead. It does essentially the same thing, but more efficiently - val q = + val metrics = for { - n <- NodesTQ.groupBy(_.orgid) - } yield (n._1, n._2.length) // this should returin [orgid, num of nodes in that orgid] + numNodesByOrg <- + if(!identity.isSuperUser && identity.isHubAdmin) + Compiled(OrgsTQ.map(organization => (organization.orgid, -1)) + .sortBy(_._1)) + .result + else { + Compiled(OrgsTQ.filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(organization => (organization.orgid === identity.getOrg || organization.orgid === "IBM")) + .filterIf(identity.role.equals(AuthRoles.Node))(_.orgid === identity.identityString) + .map(_.orgid) + .joinLeft(NodesTQ.filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(node => (node.orgid === identity.getOrg || node.orgid === "IBM")) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.owner === identity.identityString) + .filterIf(identity.role.equals(AuthRoles.Node))(_.id === identity.identityString) + .groupBy(node => node.orgid) + .map{case (orgid, group) => (orgid, group.map(_.id).length)}) + .on((organization, node) => (organization === node._1)) + .map(result => (result._1, result._2.getOrElse(("", 0))._2)) + .sortBy(_._1.asc)) + .result + } + } yield(numNodesByOrg) - db.run(q.result.asTry) - .map({ - case Success(nodes) => // nodes : Seq[(String, Int)] - orgStatusResp.nodesByOrg = nodes.toMap - orgStatusResp.msg = ExchMsg.translate("exchange.server.operating.normally") - (HttpCode.OK, orgStatusResp.toGetAdminOrgStatusResponse) - case Failure(t: org.postgresql.util.PSQLException) => - orgStatusResp.msg = t.getMessage - if (t.getMessage.contains("An I/O error occurred while sending to the backend")) - (HttpCode.BAD_GW, orgStatusResp.toGetAdminOrgStatusResponse) - else - (HttpCode.INTERNAL_ERROR, orgStatusResp.toGetAdminOrgStatusResponse) - case Failure(t) => - orgStatusResp.msg = t.getMessage - (HttpCode.INTERNAL_ERROR, orgStatusResp.toGetAdminOrgStatusResponse) - }) + db.run(metrics.transactionally.asTry).map { + case Success(result) => + (HttpCode.OK, new AdminOrgStatus(msg = ExchMsg.translate("exchange.server.operating.normally"), nodes = result.toMap)) + case Failure(t: org.postgresql.util.PSQLException) => + if (t.getMessage.contains("An I/O error occurred while sending to the backend")) + (HttpCode.BAD_GW, ApiResponse(ApiRespType.BAD_GW, t.getMessage)) + else + (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, t.getMessage)) + case Failure(t) => + (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, t.getMessage)) + } }) } @@ -72,8 +84,8 @@ trait OrganizationStatus extends JacksonSupport with AuthenticationSupport { path("admin" / "orgstatus") { get { exchAuth(TAction(), Access.ORGSTATUS) { - _ => - getOrganizationStatus + identity => + getOrganizationStatus(identity = identity) } } } diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/administration/Status.scala b/src/main/scala/org/openhorizon/exchangeapi/route/administration/Status.scala index e2f24b49..19403259 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/administration/Status.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/administration/Status.scala @@ -5,11 +5,14 @@ import org.apache.pekko.event.LoggingAdapter import org.apache.pekko.http.scaladsl.server.Directives._ import org.apache.pekko.http.scaladsl.server.{Directives, Route} import com.github.pjfanning.pekkohttpjackson.JacksonSupport +import io.swagger.v3.oas.annotations.enums.ParameterIn +import io.swagger.v3.oas.annotations.extensions.Extension import io.swagger.v3.oas.annotations.media.{Content, Schema} -import io.swagger.v3.oas.annotations.{Operation, responses} -import jakarta.ws.rs.{GET, Path} +import io.swagger.v3.oas.annotations.parameters.RequestBody +import io.swagger.v3.oas.annotations.{Hidden, Operation, Parameter, responses} +import jakarta.ws.rs.{GET, Path, Produces} import org.checkerframework.checker.units.qual.t -import org.openhorizon.exchangeapi.auth.{Access, AuthenticationSupport, TAction} +import org.openhorizon.exchangeapi.auth.{Access, AuthRoles, AuthenticationSupport, Identity, Role, TAction, TOrg} import org.openhorizon.exchangeapi.table.agreementbot.AgbotsTQ import org.openhorizon.exchangeapi.table.node.NodesTQ import org.openhorizon.exchangeapi.table.node.agreement.NodeAgreementsTQ @@ -17,16 +20,18 @@ import org.openhorizon.exchangeapi.table.node.message.NodeMsgsTQ import org.openhorizon.exchangeapi.table.user.UsersTQ import org.openhorizon.exchangeapi.table.agreementbot.agreement.AgbotAgreementsTQ import org.openhorizon.exchangeapi.table.agreementbot.message.AgbotMsgsTQ +import org.openhorizon.exchangeapi.table.organization.OrgsTQ import org.openhorizon.exchangeapi.table.schema.SchemaTQ -import org.openhorizon.exchangeapi.utility.{ExchMsg, HttpCode} +import org.openhorizon.exchangeapi.utility.{ApiRespType, ApiResponse, ExchMsg, HttpCode} +import slick.dbio.DBIOAction import slick.jdbc.PostgresProfile.api._ +import slick.lifted.Compiled +import java.lang.annotation.Annotation import scala.concurrent.ExecutionContext import scala.util.{Failure, Success} -@Path("/v1/admin/status") -@io.swagger.v3.oas.annotations.tags.Tag(name = "administration") trait Status extends JacksonSupport with AuthenticationSupport { // Will pick up these values when it is mixed in wDirectives.ith ExchangeApiApp def db: Database @@ -35,7 +40,168 @@ trait Status extends JacksonSupport with AuthenticationSupport { implicit def executionContext: ExecutionContext - // =========== GET /admin/status =============================== + def getStatus(@Parameter(hidden = true) identity: Identity, @Parameter(hidden = true) organization: Option[String] = None): Route = { // Hides fields from being included in the swagger doc as request parameters. + logger.debug("Doing GET /admin/status") + complete({ + val metrics = + for { + numOrganizations <- + Compiled(OrgsTQ.filterOpt(organization)((org, organization) => org.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isHubAdmin))(org => org.orgid === organization.getOrElse(identity.getOrg) || org.orgid === "IBM") + .map(_.orgid) + .length) + .result + + _ <- + if (numOrganizations == 0) + DBIO.failed(new NoSuchElementException(organization.getOrElse(""))) + else + DBIO.successful(0) + + numAgbots <- + Compiled(AgbotsTQ.filterOpt(organization)((agbot, organization) => agbot.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isHubAdmin || identity.isMultiTenantAgbot))(agbot => agbot.orgid === organization.getOrElse(identity.getOrg) || agbot.orgid === "IBM") + .map(agbot => (agbot.id, agbot.orgid)) + .length) + .result + + numAgbotAgeements <- + Compiled(AgbotsTQ.filterOpt(organization)((agbot, organization) => agbot.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isHubAdmin || identity.isMultiTenantAgbot))(agbot => agbot.orgid === organization.getOrElse(identity.getOrg) || agbot.orgid === "IBM") + .map(agbot => (agbot.id)) + .join(AgbotAgreementsTQ.map(agreement => (agreement.agrId, agreement.agbotId))) + .on((agbot, agreement) => (agbot === agreement._2)) + .map(result => (result._2._1)) + .length) + .result + + numAgbotMessages <- + Compiled(AgbotsTQ.filterOpt(organization)((agbot, organization) => agbot.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isHubAdmin || identity.isMultiTenantAgbot))(agbot => agbot.orgid === organization.getOrElse(identity.getOrg) || agbot.orgid === "IBM") + .map(agbot => (agbot.id)) + .join(AgbotMsgsTQ.map(message => (message.agbotId, message.msgId))) + .on((agbot, message) => (agbot === message._1)) + .map(result => (result._2._2)) + .length) + .result + + numNodes <- + if(!identity.isSuperUser && identity.isHubAdmin) + DBIO.successful(-1) + else + Compiled(NodesTQ.filterOpt(organization)((node, organization) => node.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(node => node.orgid === organization.getOrElse(identity.getOrg)) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.owner === identity.identityString) + .filterIf(identity.role.equals(AuthRoles.Node))(_.id === identity.identityString) + .map(node => (node.id, node.orgid)) + .length) + .result + + numNodesRegistered <- + if(!identity.isSuperUser && identity.isHubAdmin) + DBIO.successful(-1) + else + Compiled(NodesTQ.filterOpt(organization)((org, organization) => org.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(org => org.orgid === organization.getOrElse(identity.getOrg)) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.owner === identity.identityString) + .filterIf(identity.role.equals(AuthRoles.Node))(_.id === identity.identityString) + .filter(_.publicKey =!= "") + .map(node => (node.id, node.orgid)) + .length) + .result + + numNodesUnregistered <- + if(!identity.isSuperUser && identity.isHubAdmin) + DBIO.successful(-1) + else + Compiled(NodesTQ.filterOpt(organization)((node, organization) => node.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(node => node.orgid === organization.getOrElse(identity.getOrg)) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.owner === identity.identityString) + .filterIf(identity.role.equals(AuthRoles.Node))(_.id === identity.identityString) + .filter(_.publicKey === "") + .map(node => (node.id, node.orgid)) + .length) + .result + + numNodeAgreements <- + if(!identity.isSuperUser && identity.isHubAdmin) + DBIO.successful(-1) + else + Compiled(NodesTQ.filterOpt(organization)((node, organization) => node.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(node => node.orgid === organization.getOrElse(identity.getOrg)) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.owner === identity.identityString) + .filterIf(identity.role.equals(AuthRoles.Node))(_.id === identity.identityString) + .map(node => (node.id)) + .join(NodeAgreementsTQ.map(agreement => (agreement.agId, agreement.nodeId))) + .on((node, agreement) => (node === agreement._2)) + .map(result => (result._2._1)) + .length) + .result + + numNodeMessages <- + if(!identity.isSuperUser && identity.isHubAdmin) + DBIO.successful(-1) + else + Compiled(NodesTQ.filterOpt(organization)((node, organization) => node.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isMultiTenantAgbot))(node => node.orgid === organization.getOrElse(identity.getOrg)) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.owner === identity.identityString) + .filterIf(identity.role.equals(AuthRoles.Node))(_.id === identity.identityString) + .map(node => (node.id)) + .join(NodeMsgsTQ.map(message => (message.msgId, message.nodeId))) + .on((node, message) => (node === message._2)) + .map(result => (result._2._1)) + .length) + .result + + numUsers <- + if (identity.role.equals(AuthRoles.Agbot) || identity.role.equals(AuthRoles.Node)) + DBIO.successful(-1) + else + Compiled(UsersTQ.filterOpt(organization)((user, organization) => user.orgid === organization) + .filterIf(!(identity.isSuperUser || identity.isHubAdmin))(user => user.orgid === organization.getOrElse(identity.getOrg)) + .filterIf(!(identity.isAdmin || identity.isHubAdmin) && identity.role.equals(AuthRoles.User))(_.username === identity.identityString) + .map(user => (user.orgid, user.username)) + .length) + .result + + versionSchema <- + if (identity.isSuperUser || identity.isHubAdmin) + Compiled(SchemaTQ.filter(_.id === 0) + .map(_.schemaversion)).result.head + else + DBIO.successful(-1) + } yield(numAgbotAgeements, + numAgbotMessages, + numAgbots, + numNodeAgreements, + numNodeMessages, + numNodes, + (if (numNodesRegistered == -1) None else Option(numNodesRegistered)), + (if (numNodesUnregistered == -1) None else Option(numNodesUnregistered)), + numOrganizations, + numUsers, + versionSchema) + + db.run(metrics.transactionally.asTry).map { + case Success(result) => + (HttpCode.OK, new AdminStatus(DBMetrics = result, message = ExchMsg.translate("exchange.server.operating.normally"), organization.nonEmpty)) + case Failure(t: org.postgresql.util.PSQLException) => + if (t.getMessage.contains("An I/O error occurred while sending to the backend")) + (HttpCode.BAD_GW, ApiResponse(ApiRespType.BAD_GW, t.getMessage)) + else + (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, t.getMessage)) + case Failure(t: NoSuchElementException) => // Organization not found + (HttpCode.NOT_FOUND, ApiResponse(ApiRespType.NOT_FOUND, ExchMsg.translate("org.not.found", organization.getOrElse("")))) + case Failure(t) => + (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, t.getMessage)) + } + }) + } + + + // =========== GET /admin/status ============================================== + @Path("/v1/admin/status") + @io.swagger.v3.oas.annotations.tags.Tag(name = "administration") @GET @Operation(summary = "Returns status of the Exchange server", description = """Returns a dictionary of statuses/statistics. Can be run by any user.""", @@ -43,81 +209,47 @@ trait Status extends JacksonSupport with AuthenticationSupport { Array(new responses.ApiResponse(responseCode = "200", description = "response body", content = Array(new Content(mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAdminStatusResponse])))), + schema = new Schema(implementation = classOf[AdminStatus])))), new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"))) - def getStatus: Route = { - logger.debug("Doing GET /admin/status") - complete({ - val statusResp = new AdminStatus() //perf: use a DBIO.sequence instead. It does essentially the same thing, but more efficiently - db.run( - UsersTQ.length.result.asTry - .flatMap({ - case Success(v) => - statusResp.numberOfUsers = v - NodesTQ.length.result.asTry - case Failure(t) => - DBIO.failed(t).asTry}) - .flatMap({ - case Success(v) => - statusResp.numberOfNodes = v - AgbotsTQ.length.result.asTry - case Failure(t) => - DBIO.failed(t).asTry}) - .flatMap({ - case Success(v) => - statusResp.numberOfAgbots = v - NodeAgreementsTQ.length.result.asTry - case Failure(t) => - DBIO.failed(t).asTry}) - .flatMap({ - case Success(v) => - statusResp.numberOfNodeAgreements = v - AgbotAgreementsTQ.length.result.asTry - case Failure(t) => - DBIO.failed(t).asTry}) - .flatMap({ - case Success(v) => - statusResp.numberOfAgbotAgreements = v - NodeMsgsTQ.length.result.asTry - case Failure(t) => - DBIO.failed(t).asTry}) - .flatMap({ - case Success(v) => - statusResp.numberOfNodeMsgs = v - AgbotMsgsTQ.length.result.asTry - case Failure(t) => - DBIO.failed(t).asTry}) - .flatMap({ - case Success(v) => - statusResp.numberOfAgbotMsgs = v - SchemaTQ.getSchemaVersion.result.asTry - case Failure(t) => - DBIO.failed(t).asTry})) - .map({ - case Success(v) => - statusResp.dbSchemaVersion = v.head - statusResp.msg = ExchMsg.translate("exchange.server.operating.normally") - (HttpCode.OK, statusResp.toGetAdminStatusResponse) - case Failure(t: org.postgresql.util.PSQLException) => - if (t.getMessage.contains("An I/O error occurred while sending to the backend")) - (HttpCode.BAD_GW, statusResp.toGetAdminStatusResponse) - else - (HttpCode.INTERNAL_ERROR, statusResp.toGetAdminStatusResponse) - case Failure(t) => - statusResp.msg = t.getMessage - (HttpCode.INTERNAL_ERROR, statusResp.toGetAdminStatusResponse) - }) - }) - } - - val status: Route = + def adminStatus: Route = { path("admin" / "status") { get { exchAuth(TAction(), Access.STATUS) { - _ => - getStatus + identity => + getStatus(identity = identity) } } } + } + + // =========== GET /orgs/{organization}/status ================================ + @Path("/v1/orgs/{organization}/status") + @io.swagger.v3.oas.annotations.tags.Tag(name = "organization") + @GET + @Operation(description = "Returns the totals of key resources in the org. Can be run by any id in this org or a hub admin.", + parameters = Array(new Parameter(description = "Organization id.", in = ParameterIn.PATH, name = "organization")), + responses = + Array(new responses.ApiResponse(content = Array(new Content(mediaType = "application/json", + schema = new Schema(implementation = classOf[AdminStatus]))), + description = "response body", + responseCode = "200"), + new responses.ApiResponse(description = "invalid credentials", responseCode = "401"), + new responses.ApiResponse(description = "access denied", responseCode = "403"), + new responses.ApiResponse(description = "not found", responseCode = "404")), + summary = "Returns summary status of the org") + def orgStatus: Route = { + path("orgs" / Segment /"status") { + organization => + get { + exchAuth(TOrg(organization), Access.READ) { + identity => + getStatus(identity = identity, organization = Option(organization)) + } + } + } + } + + val status: Route = + adminStatus ~ orgStatus } diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agent/AgentConfigurationManagement.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agent/AgentConfigurationManagement.scala index c55fcc5a..c0cd5efc 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agent/AgentConfigurationManagement.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agent/AgentConfigurationManagement.scala @@ -49,7 +49,7 @@ trait AgentConfigurationManagement extends JacksonSupport with AuthenticationSup new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteAgentConfigMgmt(organization: String): Route = + def deleteAgentConfigMgmt(@Parameter(hidden = true) organization: String): Route = delete { logger.debug(s"DELETE /orgs/$organization/AgentFileVersion") complete({ @@ -141,7 +141,7 @@ trait AgentConfigurationManagement extends JacksonSupport with AuthenticationSup new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getAgentConfigMgmt(orgId: String): Route = + def getAgentConfigMgmt(@Parameter(hidden = true) orgId: String): Route = { logger.debug(s"GET /orgs/$orgId/AgentFileVersion") complete({ @@ -215,7 +215,7 @@ trait AgentConfigurationManagement extends JacksonSupport with AuthenticationSup new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putAgentConfigMgmt(organization: String): Route = + def putAgentConfigMgmt(@Parameter(hidden = true) organization: String): Route = put { entity(as[AgentVersionsRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreement/Confirm.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreement/Confirm.scala index 5da12af1..e811677b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreement/Confirm.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreement/Confirm.scala @@ -76,8 +76,8 @@ trait Confirm extends JacksonSupport with AuthenticationSupport { ) ) ) - def postConfirm(ident: Identity, - orgid: String, + def postConfirm(@Parameter(hidden = true) ident: Identity, + @Parameter(hidden = true) orgid: String, reqBody: PostAgreementsConfirmRequest): Route = complete({ val creds = ident.creds diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgbotsRoutes.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgbotsRoutes.scala deleted file mode 100644 index e5dad2e1..00000000 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgbotsRoutes.scala +++ /dev/null @@ -1,1534 +0,0 @@ -/** Services routes for all of the /agbots api methods. */ -package org.openhorizon.exchangeapi.route.agreementbot - -import com.github.pjfanning.pekkohttpjackson._ -import org.apache.pekko.actor.ActorSystem -import org.apache.pekko.event.LoggingAdapter -import org.openhorizon.exchangeapi.auth.AuthenticationSupport -import slick.jdbc.PostgresProfile.api._ - -import scala.concurrent.ExecutionContext - -//====== These are the input and output structures for /agbots routes. Swagger and/or json seem to require they be outside the trait. - -//final case class PostAgbotsIsRecentDataRequest(secondsStale: Int, agreementIds: List[String]) // the strings in the list are agreement ids -//final case class PostAgbotsIsRecentDataElement(agreementId: String, recentData: Boolean) - - -/** Implementation for all of the /agbots routes */ -//@Path("/v1/orgs/{organization}/agbots") -trait AgbotsRoutes extends JacksonSupport with AuthenticationSupport { - // Will pick up these values when it is mixed in with ExchangeApiApp - def db: Database - def system: ActorSystem - def logger: LoggingAdapter - implicit def executionContext: ExecutionContext - - //def agbotsRoutes: Route = //agbotDeleteAgreementRoute ~ - //agbotDeleteAgreementsRoute ~ - //agbotDeleteBusPolRoute ~ - //agbotDeleteBusPolsRoute ~ - //agbotDeleteMsgRoute ~ - //agbotDeletePatRoute ~ - //agbotDeletePatsRoute ~ - //agbotDeleteRoute ~ - //agbotGetAgreementRoute ~ - //agbotGetAgreementsRoute ~ - //agbotGetBusPolRoute ~ - //agbotGetBusPolsRoute ~ - //agbotGetMsgRoute ~ - //agbotGetMsgsRoute ~ - //agbotGetPatternRoute ~ - //agbotGetPatternsRoute ~ - //agbotGetRoute ~ - //agbotHeartbeatRoute ~ - //agbotPatchRoute ~ - //agbotPostBusPolRoute ~ - //agbotPostMsgRoute ~ - //agbotPostPatRoute ~ - //agbotPutAgreementRoute ~ - //agbotPutRoute ~ - //agbotsGetRoute - - /* - /* ====== GET /orgs/{organization}/agbots ================================ */ - @GET - @Path("") - @Operation(summary = "Returns all agbots", description = "Returns all agbots (Agreement Bots). Can be run by any user.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "idfilter", in = ParameterIn.QUERY, required = false, description = "Filter results to only include agbots with this id (can include % for wildcard - the URL encoding for % is %25)"), - new Parameter(name = "name", in = ParameterIn.QUERY, required = false, description = "Filter results to only include agbots with this name (can include % for wildcard - the URL encoding for % is %25)"), - new Parameter(name = "owner", in = ParameterIn.QUERY, required = false, description = "Filter results to only include agbots with this owner (can include % for wildcard - the URL encoding for % is %25)")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ - "agbots": { - "orgid/agbotname": { - "token": "string", - "name": "string", - "owner": "string", - "msgEndPoint": "", - "lastHeartbeat": "2020-05-27T19:01:10.713Z[UTC]", - "publicKey": "string" - } - }, - "lastIndex": 0 -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def agbotsGetRoute: Route = (path("orgs" / Segment / "agbots") & get & parameter("idfilter".?, "name".?, "owner".?)) { (orgid, idfilter, name, owner) => - logger.debug(s"Doing GET /orgs/$orgid/agbots") - exchAuth(TAgbot(OrgAndId(orgid,"*").toString), Access.READ) { ident => - complete({ - logger.debug(s"GET /orgs/$orgid/agbots identity: ${ident.creds.id}") // can't display the whole ident object, because that contains the pw/token - var q = AgbotsTQ.getAllAgbots(orgid) - idfilter.foreach(id => { if (id.contains("%")) q = q.filter(_.id like id) else q = q.filter(_.id === id) }) - name.foreach(name => { if (name.contains("%")) q = q.filter(_.name like name) else q = q.filter(_.name === name) }) - owner.foreach(owner => { if (owner.contains("%")) q = q.filter(_.owner like owner) else q = q.filter(_.owner === owner) }) - db.run(q.result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots result size: ${list.size}") - val agbots: Map[String, Agbot] = list.map(e => e.id -> e.toAgbot(ident.isSuperUser)).toMap - val code: StatusCode = if (agbots.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotsResponse(agbots, 0)) - }) - }) // end of complete - } // end of exchAuth - } - - /* ====== GET /orgs/{organization}/agbots/{id} ================================ */ - @GET - @Path("{id}") - @Operation(summary = "Returns an agbot", description = "Returns the agbot (Agreement Bot) with the specified id. Can be run by a user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "attribute", in = ParameterIn.QUERY, required = false, description = "Which attribute value should be returned. Only 1 attribute can be specified. If not specified, the entire node resource (including services) will be returned")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ - "agbots": { - "orgid/agbotname": { - "token": "string", - "name": "string", - "owner": "string", - "msgEndPoint": "", - "lastHeartbeat": "2020-05-27T19:01:10.713Z[UTC]", - "publicKey": "string" - } - }, - "lastIndex": 0 -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def agbotGetRoute: Route = (path("orgs" / Segment / "agbots" / Segment) & get & parameter("attribute".?)) {(orgid, id, attribute) => - logger.debug(s"Doing GET /orgs/$orgid/agbots/$id") - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId), Access.READ) { ident => - val q = if (attribute.isDefined) AgbotsTQ.getAttribute(compositeId, attribute.get) else null - validate(attribute.isEmpty || q!= null, ExchMsg.translate("agbot.name.not.in.resource")) { - complete({ - logger.debug(s"GET /orgs/$orgid/agbots/$id identity: ${ident.creds.id}") // can't display the whole ident object, because that contains the pw/token - attribute match { - case Some(attr) => // Only returning 1 attr of the agbot - db.run(q.result).map({ list => - //logger.debug("GET /orgs/"+orgid+"/agbots/"+id+" attribute result: "+list.toString) - if (list.nonEmpty) (HttpCode.OK, GetAgbotAttributeResponse(attr, list.head.toString)) - else (HttpCode.NOT_FOUND, ApiResponse(ApiRespType.NOT_FOUND, ExchMsg.translate("not.found"))) // validateAccessToAgbot() will return ApiRespType.NOT_FOUND to the client so do that here for consistency - }) - - case None => // Return the whole agbot, including the services - db.run(AgbotsTQ.getAgbot(compositeId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots result size: ${list.size}") - val agbots: Map[String, Agbot] = list.map(e => e.id -> e.toAgbot(ident.isSuperUser)).toMap - val code: StatusCode = if (agbots.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotsResponse(agbots, 0)) - }) - } - }) // end of complete - } // end of validate - } // end of exchAuth - } - - // =========== PUT /orgs/{organization}/agbots/{id} =============================== - @PUT - @Path("{id}") - @Operation( - summary = "Add/updates an agbot", - description = "Adds a new agbot (Agreement Bot) to the exchange DB, or updates an existing agbot. This must be called by the user to add an agbot, and then can be called by that user or agbot to update itself.", - parameters = Array( - new Parameter( - name = "organization", - in = ParameterIn.PATH, - description = "Organization id." - ), - new Parameter( - name = "id", - in = ParameterIn.PATH, - description = "ID of the agbot." - ) - ), - requestBody = new RequestBody( - content = Array( - new Content( - examples = Array( - new ExampleObject( - value = """{ - "token": "abc", - "name": "myagbot", - "publicKey": "ABCDEF" -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[PutAgbotsRequest]) - ) - ), - required = true - ), - responses = Array( - new responses.ApiResponse( - responseCode = "200", - description = "resource add/updated - response body:", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse]))) - ), - new responses.ApiResponse( - responseCode = "400", - description = "bad input" - ), - new responses.ApiResponse( - responseCode = "401", - description = "invalid credentials" - ), - new responses.ApiResponse( - responseCode = "403", - description = "access denied" - ), - new responses.ApiResponse( - responseCode = "404", - description = "not found" - ) - ) - ) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def agbotPutRoute: Route = (path("orgs" / Segment / "agbots" / Segment) & put & entity(as[PutAgbotsRequest])) { (orgid, id, reqBody) => - logger.debug(s"Doing PUT /orgs/$orgid/agbots/$id") - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { ident => - validateWithMsg(reqBody.getAnyProblem) { - complete({ - val owner: String = ident match { case IUser(creds) => creds.id; case _ => "" } - val hashedTok: String = Password.hash(reqBody.token) - db.run(AgbotsTQ.getNumOwned(owner).result.flatMap({ xs => - logger.debug("PUT /orgs/"+orgid+"/agbots/"+id+" num owned: "+xs) - val numOwned: Int = xs - val maxAgbots: Int = ExchConfig.getInt("api.limits.maxAgbots") - if (maxAgbots == 0 || numOwned <= maxAgbots || owner == "") { // when owner=="" we know it is only an update, otherwise we are not sure, but if they are already over the limit, stop them anyway - val action = if (owner == "") reqBody.getDbUpdate(compositeId, orgid, owner, hashedTok) else reqBody.getDbUpsert(compositeId, orgid, owner, hashedTok) - action.asTry - } - else DBIO.failed(new DBProcessingError(HttpCode.ACCESS_DENIED, ApiRespType.ACCESS_DENIED, ExchMsg.translate("over.max.limit.of.agbots", maxAgbots) )).asTry - }).flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug(s"PUT /orgs/$orgid/agbots/$id result: $v") - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, public = false, ResChangeResource.AGBOT, ResChangeOperation.CREATEDMODIFIED).insert.asTry - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug(s"PUT /orgs/$orgid/agbots/$id updated in changes table: $v") - AuthCache.putAgbotAndOwner(compositeId, hashedTok, reqBody.token, owner) - (HttpCode.PUT_OK, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.added.updated"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.not.inserted.or.updated", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.not.inserted.or.updated", compositeId, t.toString))) - }) - }) // end of complete - } // end of validateWithMsg - } // end of exchAuth - } - - // =========== PATCH /orgs/{organization}/agbots/{id} =============================== - @PATCH - @Path("{id}") - @Operation(summary = "Updates 1 attribute of an agbot", description = "Updates some attributes of an agbot. This can be called by the user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - requestBody = new RequestBody(description = "Specify only **one** of the following attributes", required = true, content = Array(new Content(examples = Array( - new ExampleObject( - value = """{ - "token": "abc", - "name": "myagbot", - "msgEndPoint": "string", - "publicKey": "ABCDEF" -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[PatchAgbotsRequest]) - ))), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "resource updated - response body:", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse])))), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def agbotPatchRoute: Route = (path("orgs" / Segment / "agbots" / Segment) & patch & entity(as[PatchAgbotsRequest])) { (orgid, id, reqBody) => - logger.debug(s"Doing PATCH /orgs/$orgid/agbots/$id") - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - validateWithMsg(reqBody.getAnyProblem) { - complete({ - val hashedTok: String = if (reqBody.token.isDefined) Password.hash(reqBody.token.get) else "" // hash the token if that is what is being updated - val (action, attrName) = reqBody.getDbUpdate(compositeId, orgid, hashedTok) - if (action == null) (HttpCode.BAD_INPUT, ApiResponse(ApiRespType.BAD_INPUT, ExchMsg.translate("no.valid.agbot.attribute.specified"))) - else db.run(action.transactionally.asTry.flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug(s"PATCH /orgs/$orgid/agbots/$id result: $v") - if (v.asInstanceOf[Int] > 0) { // there were no db errors, but determine if it actually found it or not - if (reqBody.token.isDefined) AuthCache.putAgbot(compositeId, hashedTok, reqBody.token.get) // We do not need to run putOwner because patch does not change the owner - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, public = false, ResChangeResource.AGBOT, ResChangeOperation.MODIFIED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("agbot.not.found", compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug(s"PATCH /orgs/$orgid/agbots/$id updated in changes table: $v") - (HttpCode.PUT_OK, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.attribute.updated", attrName, compositeId))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.not.inserted.or.updated", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.not.inserted.or.updated", compositeId, t.toString))) - }) - }) // end of complete - } // end of validateWithMsg - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{id} =============================== - @DELETE - @Path("{id}") - @Operation(summary = "Deletes an agbot", description = "Deletes an agbot (Agreement Bot), and deletes the agreements stored for this agbot (but does not actually cancel the agreements between the nodes and agbot). Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def agbotDeleteRoute: Route = (path("orgs" / Segment / "agbots" / Segment) & delete) { (orgid, id) => - logger.debug(s"Doing DELETE /orgs/$orgid/agbots/$id") - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - // remove does *not* throw an exception if the key does not exist - db.run(AgbotsTQ.getAgbot(compositeId).delete.transactionally.asTry.flatMap({ - case Success(v) => - if (v > 0) { // there were no db errors, but determine if it actually found it or not - logger.debug(s"DELETE /orgs/$orgid/agbots/$id result: $v") - AuthCache.removeAgbotAndOwner(compositeId) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, public = false, ResChangeResource.AGBOT, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("agbot.not.found", compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug(s"DELETE /orgs/$orgid/agbots/$id updated in changes table: $v") - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.not.deleted", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.not.deleted", compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - */ - - /*// =========== POST /orgs/{organization}/agbots/{agreementbot}/heartbeat =============================== - @POST - @Path("{id}/heartbeat") - @Operation(summary = "Tells the exchange this agbot is still operating", description = "Lets the exchange know this agbot is still active. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot to be updated.")), - responses = Array( - new responses.ApiResponse(responseCode = "201", description = "response body", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse])))), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def agbotHeartbeatRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "heartbeat") & post) { (orgid, id) => - logger.debug(s"Doing POST /orgs/$orgid/users/$id/heartbeat") - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.WRITE) { _ => - complete({ - db.run(AgbotsTQ.getLastHeartbeat(compositeId).update(ApiTime.nowUTC).asTry).map({ - case Success(v) => - if (v > 0) { // there were no db errors, but determine if it actually found it or not - logger.debug(s"POST /orgs/$orgid/users/$id/heartbeat result: $v") - (HttpCode.POST_OK, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.updated"))) - } else { - (HttpCode.NOT_FOUND, ApiResponse(ApiRespType.NOT_FOUND, ExchMsg.translate("agbot.not.found", compositeId))) - } - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.not.updated", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.not.updated", compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - }*/ - - /* - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/patterns ================================ */ - @GET - @Path("{id}/patterns") - @Operation(summary = "Returns all patterns served by this agbot", description = "Returns all patterns that this agbot is finding nodes for to make agreements with them. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ - "patterns": { - "pattern1": { - "patternOrgid": "string", - "pattern": "string", - "nodeOrgid": "string", - "lastUpdated": "2019-05-14T16:34:36.295Z[UTC]" - }, - "pattern2": { - "patternOrgid": "string", - "pattern": "string", - "nodeOrgid": "string", - "lastUpdated": "2019-05-14T16:34:36.397Z[UTC]" - } - } -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotPatternsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/pattern") - def agbotGetPatternsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "patterns") & get) { (orgid, id) => - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - complete({ - db.run(AgbotPatternsTQ.getPatterns(compositeId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots/$id/patterns result size: ${list.size}") - val patterns: Map[String, AgbotPattern] = list.map(e => e.patId -> e.toAgbotPattern).toMap - val code: StatusCode = if (patterns.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotPatternsResponse(patterns)) - }) - }) // end of complete - } // end of exchAuth - } - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/patterns/{patid} ================================ */ - @GET - @Path("{id}/patterns/{patid}") - @Operation(summary = "Returns a pattern this agbot is serving", description = "Returns the pattern with the specified patid for the specified agbot id. The patid should be in the form patternOrgid_pattern. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "patid", in = ParameterIn.PATH, description = "ID of the pattern.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ - "patterns": { - "patternname": { - "patternOrgid": "string", - "pattern": "string", - "nodeOrgid": "string", - "lastUpdated": "2019-05-14T16:34:36.397Z[UTC]" - } - } -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotPatternsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/pattern") - def agbotGetPatternRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "patterns" / Segment) & get) { (orgid, id, patId) => - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - complete({ - db.run(AgbotPatternsTQ.getPattern(compositeId, patId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots/$id/patterns/$patId result size: ${list.size}") - val patterns: Map[String, AgbotPattern] = list.map(e => e.patId -> e.toAgbotPattern).toMap - val code: StatusCode = if (patterns.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotPatternsResponse(patterns)) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== POST /orgs/{organization}/agbots/{agreementbot}/patterns =============================== - @POST - @Path("{id}/patterns") - @Operation( - summary = "Adds a pattern that the agbot should serve", - description = "Adds a new pattern and node org that this agbot should find nodes for to make agreements with them. This is called by the owning user or the agbot to give their information about the pattern.", - parameters = Array( - new Parameter( - name = "organization", - in = ParameterIn.PATH, - description = "Organization id."), - new Parameter( - name = "id", - in = ParameterIn.PATH, - description = "ID of the agbot to be updated.")), - requestBody = new RequestBody( - content = Array( - new Content( - examples = Array( - new ExampleObject( - value = """{ - "patternOrgid": "string", - "pattern": "string", - "nodeOrgid": "string" -}""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[PostAgbotPatternRequest]) - ) - ), - required = true - ), - responses = Array( - new responses.ApiResponse( - responseCode = "201", - description = "response body", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse])))), - new responses.ApiResponse( - responseCode = "401", - description = "invalid credentials"), - new responses.ApiResponse( - responseCode = "403", - description = "access denied"), - new responses.ApiResponse( - responseCode = "404", - description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/pattern") - def agbotPostPatRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "patterns") & post & entity(as[PostAgbotPatternRequest])) { (orgid, id, reqBody) => - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.WRITE) { _ => - validateWithMsg(reqBody.getAnyProblem) { - complete({ - val patId: String = reqBody.formId - db.run(PatternsTQ.getPattern(OrgAndId(reqBody.patternOrgid,reqBody.pattern).toString).length.result.asTry.flatMap({ - case Success(num) => - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/patterns pattern validation: " + num) - if (num > 0 || reqBody.pattern == "*") reqBody.toAgbotPatternRow(compositeId, patId).insert.asTry - else DBIO.failed(new Throwable(ExchMsg.translate("pattern.not.in.exchange"))).asTry - case Failure(t) => DBIO.failed(t).asTry - }).flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/patterns result: " + v) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTPATTERNS, ResChangeOperation.CREATED).insert.asTry - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/patterns updated in changes table: " + v) - (HttpCode.POST_OK, ApiResponse(ApiRespType.OK, ExchMsg.translate("pattern.added", patId))) - case Failure(t: org.postgresql.util.PSQLException) => - if (ExchangePosgtresErrorHandling.isDuplicateKeyError(t)) (HttpCode.ALREADY_EXISTS2, ApiResponse(ApiRespType.ALREADY_EXISTS, ExchMsg.translate("pattern.foragbot.already.exists", patId, compositeId))) - else if (ExchangePosgtresErrorHandling.isAccessDeniedError(t)) (HttpCode.ACCESS_DENIED, ApiResponse(ApiRespType.ACCESS_DENIED, ExchMsg.translate("pattern.not.inserted", patId, compositeId, t.getMessage))) - else ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("pattern.not.inserted", patId, compositeId, t.getServerErrorMessage)) - case Failure(t) => - if (t.getMessage.startsWith("Access Denied:")) (HttpCode.ACCESS_DENIED, ApiResponse(ApiRespType.ACCESS_DENIED, ExchMsg.translate("pattern.not.inserted", patId, compositeId, t.getMessage))) - else (HttpCode.BAD_INPUT, ApiResponse(ApiRespType.BAD_INPUT, ExchMsg.translate("pattern.not.inserted", patId, compositeId, t.getMessage))) - }) - }) // end of complete - } // end of validateWithMsg - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/patterns =============================== - @DELETE - @Path("{id}/patterns") - @Operation(summary = "Deletes all patterns of an agbot", description = "Deletes all of the current patterns that this agbot was serving. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/pattern") - def agbotDeletePatsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "patterns") & delete) { (orgid, id) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - // remove does *not* throw an exception if the key does not exist - db.run(AgbotPatternsTQ.getPatterns(compositeId).delete.asTry.flatMap({ - case Success(v) => - if (v > 0) { // there were no db errors, but determine if it actually found it or not - // Add the resource to the resourcechanges table - logger.debug("DELETE /agbots/" + id + "/patterns result: " + v) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTPATTERNS, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("patterns.not.found", compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/patterns updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("patterns.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("patterns.not.deleted", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("patterns.not.deleted", compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/patterns/{patid} =============================== - @DELETE - @Path("{id}/patterns/{patid}") - @Operation(summary = "Deletes a pattern of an agbot", description = "Deletes a pattern that this agbot was serving. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "patid", in = ParameterIn.PATH, description = "ID of the pattern to be deleted.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/pattern") - def agbotDeletePatRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "patterns" / Segment) & delete) { (orgid, id, patId) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - db.run(AgbotPatternsTQ.getPattern(compositeId,patId).delete.asTry.flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("DELETE /agbots/" + id + "/patterns/" + patId + " result: " + v) - if (v > 0) { // there were no db errors, but determine if it actually found it or not - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTPATTERNS, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("pattern.not.found", patId, compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/patterns/" + patId + " updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.pattern.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("pattern.not.deleted", patId, compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("pattern.not.deleted", patId, compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - */ - - /* - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/businesspols ================================ */ - @GET - @Path("{id}/businesspols") - @Operation(summary = "Returns all business policies served by this agbot", description = "Returns all business policies that this agbot is finding nodes for to make agreements with them. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ -"businessPols" : { - "buspolid": { - "businessPolOrgid": "string", - "businessPol": "string", - "nodeOrgid" : "string", - "lastUpdated": "string" - } -} -}""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotBusinessPolsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/policy") - def agbotGetBusPolsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "businesspols") & get) { (orgid, id) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - complete({ - db.run(AgbotBusinessPolsTQ.getBusinessPols(compositeId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots/$id/businesspols result size: ${list.size}") - val businessPols = list.map(e => e.busPolId -> e.toAgbotBusinessPol).toMap - val code = if (businessPols.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotBusinessPolsResponse(businessPols)) - }) - }) // end of complete - } // end of exchAuth - } - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/businesspols/{buspolid} ================================ */ - @GET - @Path("{id}/businesspols/{buspolid}") - @Operation(summary = "Returns a business policy this agbot is serving", description = "Returns the business policy with the specified patid for the specified agbot id. The patid should be in the form businessPolOrgid_businessPol. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "buspolid", in = ParameterIn.PATH, description = "ID of the business policy.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ -"businessPols" : { - "buspolid": { - "businessPolOrgid": "string", - "businessPol": "string", - "nodeOrgid" : "string", - "lastUpdated": "string" - } -} -}""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotBusinessPolsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/policy") - def agbotGetBusPolRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "businesspols" / Segment) & get) { (orgid, id, busPolId) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - complete({ - db.run(AgbotBusinessPolsTQ.getBusinessPol(compositeId, busPolId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots/$id/businesspols/$busPolId result size: ${list.size}") - val businessPols = list.map(e => e.busPolId -> e.toAgbotBusinessPol).toMap - val code = if (businessPols.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotBusinessPolsResponse(businessPols)) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== POST /orgs/{organization}/agbots/{agreementbot}/businesspols =============================== - @POST - @Path("{id}/businesspols") - @Operation( - summary = "Adds a business policy that the agbot should serve", - description = "Adds a new business policy and node org that this agbot should find nodes for to make agreements with them. This is called by the owning user or the agbot to give their information about the business policy.", - parameters = Array( - new Parameter( - name = "organization", - in = ParameterIn.PATH, - description = "Organization id." - ), - new Parameter( - name = "id", - in = ParameterIn.PATH, - description = "ID of the agbot to be updated." - ) - ), - requestBody = new RequestBody( - content = Array( - new Content( - examples = Array( - new ExampleObject( - value = """{ - "businessPolOrgid": "string", - "businessPol": "string", - "nodeOrgid": "string" -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[PostAgbotBusinessPolRequest]) - ) - ), - required = true - ), - responses = Array( - new responses.ApiResponse( - responseCode = "201", - description = "response body", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse]))) - ), - new responses.ApiResponse( - responseCode = "401", - description = "invalid credentials" - ), - new responses.ApiResponse( - responseCode = "403", - description = "access denied" - ), - new responses.ApiResponse( - responseCode = "404", - description = "not found" - ) - ) - ) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/policy") - def agbotPostBusPolRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "businesspols") & post & entity(as[PostAgbotBusinessPolRequest])) { (orgid, id, reqBody) => - val compositeId = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.WRITE) { _ => - validateWithMsg(reqBody.getAnyProblem) { - complete({ - val patId = reqBody.formId - db.run(BusinessPoliciesTQ.getBusinessPolicy(OrgAndId(reqBody.businessPolOrgid,reqBody.businessPol).toString).length.result.asTry.flatMap({ - case Success(num) => - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/businesspols business policy validation: " + num) - if (num > 0 || reqBody.businessPol == "*") reqBody.toAgbotBusinessPolRow(compositeId, patId).insert.asTry - else DBIO.failed(new Throwable(ExchMsg.translate("buspol.not.in.exchange"))).asTry - case Failure(t) => DBIO.failed(t).asTry - }).flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/businesspols result: " + v) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTBUSINESSPOLS, ResChangeOperation.CREATED).insert.asTry - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/businesspols updated in changes table: " + v) - (HttpCode.POST_OK, ApiResponse(ApiRespType.OK, ExchMsg.translate("buspol.added", patId))) - case Failure(t: org.postgresql.util.PSQLException) => - if (ExchangePosgtresErrorHandling.isDuplicateKeyError(t)) (HttpCode.ALREADY_EXISTS2, ApiResponse(ApiRespType.ALREADY_EXISTS, ExchMsg.translate("buspol.foragbot.already.exists", patId, compositeId))) - else if (ExchangePosgtresErrorHandling.isAccessDeniedError(t)) (HttpCode.ACCESS_DENIED, ApiResponse(ApiRespType.ACCESS_DENIED, ExchMsg.translate("buspol.not.inserted", patId, compositeId, t.getMessage))) - else ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("buspol.not.inserted", patId, compositeId, t.getServerErrorMessage)) - case Failure(t) => - if (t.getMessage.startsWith("Access Denied:")) (HttpCode.ACCESS_DENIED, ApiResponse(ApiRespType.ACCESS_DENIED, ExchMsg.translate("buspol.not.inserted", patId, compositeId, t.getMessage))) - else (HttpCode.BAD_INPUT, ApiResponse(ApiRespType.BAD_INPUT, ExchMsg.translate("buspol.not.inserted", patId, compositeId, t.getMessage))) - }) - }) // end of complete - } // end of validateWithMsg - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/businesspols =============================== - @DELETE - @Path("{id}/businesspols") - @Operation(summary = "Deletes all business policies of an agbot", description = "Deletes all of the current business policies that this agbot was serving. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/policy") - def agbotDeleteBusPolsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "businesspols") & delete) { (orgid, id) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - // remove does *not* throw an exception if the key does not exist - db.run(AgbotBusinessPolsTQ.getBusinessPols(compositeId).delete.asTry.flatMap({ - case Success(v) => - if (v > 0) { // there were no db errors, but determine if it actually found it or not - // Add the resource to the resourcechanges table - logger.debug("DELETE /agbots/" + id + "/businesspols result: " + v) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTBUSINESSPOLS, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("buspols.not.found", compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/businesspols updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("buspols.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("buspols.not.deleted", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("buspols.not.deleted", compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/businesspols/{buspolid} =============================== - @DELETE - @Path("{id}/businesspols/{buspolid}") - @Operation(summary = "Deletes a business policy of an agbot", description = "Deletes a business policy that this agbot was serving. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "buspolid", in = ParameterIn.PATH, description = "ID of the business policy to be deleted.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/policy") - def agbotDeleteBusPolRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "businesspols" / Segment) & delete) { (orgid, id, busPolId) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - db.run(AgbotBusinessPolsTQ.getBusinessPol(compositeId,busPolId).delete.asTry.flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("DELETE /agbots/" + id + "/businesspols/" + busPolId + " result: " + v) - if (v > 0) { // there were no db errors, but determine if it actually found it or not - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTBUSINESSPOLS, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("buspol.not.found", busPolId, compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/businesspols/" + busPolId + " updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("buspol.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("buspol.not.deleted", busPolId, compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("buspol.not.deleted", busPolId, compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - */ - - /* - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/agreements ================================ */ - @GET - @Path("{id}/agreements") - @Operation(summary = "Returns all agreements this agbot is in", description = "Returns all agreements that this agbot is part of. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ - "agreements": { - "agreementname": { - "service": { - "orgid": "string", - "pattern": "string", - "url": "string" - }, - "state": "string", - "lastUpdated": "2019-05-14T16:34:37.173Z[UTC]", - "dataLastReceived": "" - } - }, - "lastIndex": 0 -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotAgreementsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def agbotGetAgreementsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "agreements") & get) { (orgid, id) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - complete({ - db.run(AgbotAgreementsTQ.getAgreements(compositeId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots/$id/agreements result size: ${list.size}") - val agreements = list.map(e => e.agrId -> e.toAgbotAgreement).toMap - val code = if (agreements.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotAgreementsResponse(agreements, 0)) - }) - }) // end of complete - } // end of exchAuth - } - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/agreements/{agid} ================================ */ - @GET - @Path("{id}/agreements/{agid}") - @Operation(summary = "Returns an agreement for an agbot", description = "Returns the agreement with the specified agid for the specified agbot id. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "agid", in = ParameterIn.PATH, description = "ID of the agreement.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array( - new Content( - examples = Array( - new ExampleObject( - value ="""{ - "agreements": { - "agreementname": { - "service": { - "orgid": "string", - "pattern": "string", - "url": "string" - }, - "state": "string", - "lastUpdated": "2019-05-14T16:34:37.173Z[UTC]", - "dataLastReceived": "" - } - }, - "lastIndex": 0 -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[GetAgbotAgreementsResponse]) - ) - )), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def agbotGetAgreementRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "agreements" / Segment) & get) { (orgid, id, agrId) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - complete({ - db.run(AgbotAgreementsTQ.getAgreement(compositeId, agrId).result).map({ list => - logger.debug(s"GET /orgs/$orgid/agbots/$id/agreements/$agrId result size: ${list.size}") - val agreements = list.map(e => e.agrId -> e.toAgbotAgreement).toMap - val code = if (agreements.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotAgreementsResponse(agreements, 0)) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== PUT /orgs/{organization}/agbots/{agreementbot}/agreements/{agid} =============================== - @PUT - @Path("{id}/agreements/{agid}") - @Operation( - summary = "Adds/updates an agreement of an agbot", - description = "Adds a new agreement of an agbot to the exchange DB, or updates an existing agreement. This is called by the owning user or the agbot to give their information about the agreement.", - parameters = Array( - new Parameter( - name = "organization", - in = ParameterIn.PATH, - description = "Organization id." - ), - new Parameter( - name = "id", - in = ParameterIn.PATH, - description = "ID of the agbot to be updated." - ), - new Parameter( - name = "agid", - in = ParameterIn.PATH, - description = "ID of the agreement to be added/updated." - ) - ), - requestBody = new RequestBody( - content = Array( - new Content( - examples = Array( - new ExampleObject( - value = """{ - "service": { - "orgid": "string", - "pattern": "string", - "url": "string" - }, - "state": "string" -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[PutAgbotAgreementRequest]) - ) - ), - required = true - ), - responses = Array( - new responses.ApiResponse( - responseCode = "201", - description = "response body", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse]))) - ), - new responses.ApiResponse( - responseCode = "401", - description = "invalid credentials" - ), - new responses.ApiResponse( - responseCode = "403", - description = "access denied" - ), - new responses.ApiResponse( - responseCode = "404", - description = "not found" - ) - ) - ) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def agbotPutAgreementRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "agreements" / Segment) & put & entity(as[PutAgbotAgreementRequest])) { (orgid, id, agrId, reqBody) => - val compositeId = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.WRITE) { _ => - validateWithMsg(reqBody.getAnyProblem) { - complete({ - val maxAgreements = ExchConfig.getInt("api.limits.maxAgreements") - val getNumOwnedDbio = if (maxAgreements == 0) DBIO.successful(0) else AgbotAgreementsTQ.getNumOwned(compositeId).result // avoid DB read for this if there is no max - db.run(getNumOwnedDbio.flatMap({ xs => - if (maxAgreements != 0) logger.debug("PUT /orgs/"+orgid+"/agbots/"+id+"/agreements/"+agrId+" num owned: "+xs) - val numOwned = xs - // we are not sure if this is create or update, but if they are already over the limit, stop them anyway - if (maxAgreements == 0 || numOwned <= maxAgreements) reqBody.toAgbotAgreementRow(compositeId, agrId).upsert.asTry - else DBIO.failed(new DBProcessingError(HttpCode.ACCESS_DENIED, ApiRespType.ACCESS_DENIED, ExchMsg.translate("over.max.limit.of.agreements", maxAgreements) )).asTry - }).flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("PUT /orgs/" + orgid + "/agbots/" + id + "/agreements/" + agrId + " result: " + v) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTAGREEMENTS, ResChangeOperation.CREATEDMODIFIED).insert.asTry - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("PUT /orgs/" + orgid + "/agbots/" + id + "/agreements/" + agrId + " updated in changes table: " + v) - (HttpCode.PUT_OK, ApiResponse(ApiRespType.OK, ExchMsg.translate("agreement.added.or.updated"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agreement.not.inserted.or.updated", agrId, compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agreement.not.inserted.or.updated", agrId, compositeId, t.toString))) - }) - }) // end of complete - } // end of validateWithMsg - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/agreements =============================== - @DELETE - @Path("{id}/agreements") - @Operation(summary = "Deletes all agreements of an agbot", description = "Deletes all of the current agreements of an agbot. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def agbotDeleteAgreementsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "agreements") & delete) { (orgid, id) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - // remove does *not* throw an exception if the key does not exist - db.run(AgbotAgreementsTQ.getAgreements(compositeId).delete.asTry.flatMap({ - case Success(v) => - if (v > 0) { // there were no db errors, but determine if it actually found it or not - // Add the resource to the resourcechanges table - logger.debug("DELETE /agbots/" + id + "/agreements result: " + v) - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTAGREEMENTS, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("no.agreements.found.for.agbot", compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/agreements updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.agreements.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.agreements.not.deleted", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.agreements.not.deleted", compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/agreements/{agid} =============================== - @DELETE - @Path("{id}/agreements/{agid}") - @Operation(summary = "Deletes an agreement of an agbot", description = "Deletes an agreement of an agbot. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "agid", in = ParameterIn.PATH, description = "ID of the agreement to be deleted.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def agbotDeleteAgreementRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "agreements" / Segment) & delete) { (orgid, id, agrId) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - db.run(AgbotAgreementsTQ.getAgreement(compositeId,agrId).delete.asTry.flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("DELETE /agbots/" + id + "/agreements/" + agrId + " result: " + v) - if (v > 0) { // there were no db errors, but determine if it actually found it or not - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTAGREEMENTS, ResChangeOperation.DELETED).insert.asTry - } else { - DBIO.failed(new DBProcessingError(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, ExchMsg.translate("agreement.for.agbot.not.found", agrId, compositeId))).asTry - } - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/agreements/" + agrId + " updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.agreement.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agreement.for.agbot.not.deleted", agrId, compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agreement.for.agbot.not.deleted", agrId, compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - */ - - /* - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // =========== POST /orgs/{organization}/agbots/{agreementbot}/msgs =============================== - @POST - @Path("{id}/msgs") - @Operation( - summary = "Sends a msg from a node to an agbot", - description = "Sends a msg from a node to an agbot. The node must 1st sign the msg (with its private key) and then encrypt the msg (with the agbots's public key). Can be run by any node.", - parameters = Array( - new Parameter( - name = "organization", - in = ParameterIn.PATH, - description = "Organization id." - ), - new Parameter( - name = "id", - in = ParameterIn.PATH, - description = "ID of the agbot to send a message to." - ) - ), - requestBody = new RequestBody( - content = Array( - new Content( - examples = Array( - new ExampleObject( - value = """{ - "message": "VW1RxzeEwTF0U7S96dIzSBQ/hRjyidqNvBzmMoZUW3hpd3hZDvs", - "ttl": 86400 -} -""" - ) - ), - mediaType = "application/json", - schema = new Schema(implementation = classOf[PostAgbotsMsgsRequest]) - ) - ), - required = true - ), - responses = Array( - new responses.ApiResponse( - responseCode = "201", - description = "response body", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[ApiResponse]))) - ), - new responses.ApiResponse( - responseCode = "401", - description = "invalid credentials" - ), - new responses.ApiResponse( - responseCode = "403", - description = "access denied" - ), - new responses.ApiResponse( - responseCode = "404", - description = "not found" - ) - ) - ) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def agbotPostMsgRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "msgs") & post & entity(as[PostAgbotsMsgsRequest])) { (orgid, id, reqBody) => - val compositeId = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.SEND_MSG_TO_AGBOT) { ident => - complete({ - val nodeId = ident.creds.id //somday: handle the case where the acls allow users to send msgs - var msgNum = "" - val maxMessagesInMailbox = ExchConfig.getInt("api.limits.maxMessagesInMailbox") - val getNumOwnedDbio = if (maxMessagesInMailbox == 0) DBIO.successful(0) else AgbotMsgsTQ.getNumOwned(compositeId).result // avoid DB read for this if there is no max - // Remove msgs whose TTL is past, then check the mailbox is not full, then get the node publicKey, then write the agbotmsgs row, all in the same db.run thread - db.run(getNumOwnedDbio.flatMap({ xs => - if (maxMessagesInMailbox != 0) logger.debug("POST /orgs/"+orgid+"/agbots/"+id+"/msgs mailbox size: "+xs) - val mailboxSize = xs - if (maxMessagesInMailbox == 0 || mailboxSize < maxMessagesInMailbox) NodesTQ.getPublicKey(nodeId).result.asTry - else DBIO.failed(new DBProcessingError(HttpCode.BAD_GW, ApiRespType.BAD_GW, ExchMsg.translate("agbot.mailbox.full", compositeId, maxMessagesInMailbox) )).asTry - }).flatMap({ - case Success(v) => - logger.debug("POST /orgs/" + orgid + "/agbots/" + id + "/msgs node publickey result: " + v) - val nodePubKey = v.head - if (nodePubKey != "") AgbotMsgRow(0, compositeId, nodeId, nodePubKey, reqBody.message, ApiTime.nowUTC, ApiTime.futureUTC(reqBody.ttl)).insert.asTry - else DBIO.failed(new DBProcessingError(HttpCode.BAD_INPUT, ApiRespType.BAD_INPUT, ExchMsg.translate("agbot.message.invalid.input"))).asTry - case Failure(t) => - DBIO.failed(t).asTry // rethrow the error to the next step - }).flatMap({ - case Success(v) => - // Add the resource to the resourcechanges table - logger.debug("POST /orgs/{organization}/agbots/" + id + "/msgs write row result: " + v) - msgNum = v.toString - ResourceChange(0L, orgid, id, ResChangeCategory.AGBOT, false, ResChangeResource.AGBOTMSGS, ResChangeOperation.CREATED).insert.asTry - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => - logger.debug("POST /orgs/{organization}/agbots/" + id + "/msgs updated in changes table: " + v) - (HttpCode.POST_OK, ApiResponse(ApiRespType.OK, "agbot msg " + msgNum + " inserted")) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - if (ExchangePosgtresErrorHandling.isKeyNotFoundError(t)) (HttpCode.NOT_FOUND, ApiResponse(ApiRespType.NOT_FOUND, ExchMsg.translate("agbot.message.agbotid.not.found", compositeId, t.getMessage))) - else ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.message.not.inserted", compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.message.not.inserted", compositeId, t.toString))) - }) - }) // end of complete - } // end of exchAuth - } - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/msgs ================================ */ - @GET - @Path("{id}/msgs") - @Operation(summary = "Returns all msgs sent to this agbot", description = "Returns all msgs that have been sent to this agbot. They will be returned in the order they were sent. All msgs that have been sent to this agbot will be returned, unless the agbot has deleted some, or some are past their TTL. Can be run by a user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "maxmsgs", in = ParameterIn.QUERY, required = false, description = "Maximum number of messages returned. If this is less than the number of messages available, the oldest messages are returned. Defaults to unlimited.") - ), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[GetAgbotMsgsResponse])))), - new responses.ApiResponse(responseCode = "400", description = "bad input"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def agbotGetMsgsRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "msgs") & get & parameter("maxmsgs".?)) { (orgid, id, maxmsgsStrOpt) => - val compositeId: String = OrgAndId(orgid, id).toString - exchAuth(TAgbot(compositeId),Access.READ) { _ => - validate(Try(maxmsgsStrOpt.map(_.toInt)).isSuccess, ExchMsg.translate("invalid.int.for.name", maxmsgsStrOpt.getOrElse(""), "maxmsgs")) { - complete({ - // Set the query, including maxmsgs - var maxIntOpt: Option[Int] = maxmsgsStrOpt.map(_.toInt) - var query = AgbotMsgsTQ.getMsgs(compositeId).sortBy(_.msgId) - if (maxIntOpt.getOrElse(0) > 0) query = query.take(maxIntOpt.get) - // Get the msgs for this agbot - db.run(query.result).map({ list => - logger.debug("GET /orgs/"+orgid+"/agbots/"+id+"/msgs result size: "+list.size) - //logger.debug("GET /orgs/"+orgid+"/agbots/"+id+"/msgs result: "+list.toString) - val msgs: List[AgbotMsg] = list.map(_.toAgbotMsg).toList - val code: StatusCode = if (msgs.nonEmpty) StatusCodes.OK else StatusCodes.NotFound - (code, GetAgbotMsgsResponse(msgs, 0)) - }) - }) // end of complete - } - } // end of exchAuth - } - - /* ====== GET /orgs/{organization}/agbots/{agreementbot}/msgs/{msgid} ================================ */ - @GET - @Path("{id}/msgs/{msgid}") - @Operation(description = "Returns A specific message that has been sent to this agreement-bot. Deleted/post-TTL (Time To Live) messages will not be returned. Can be run by a user or the agbot.", - parameters = Array(new Parameter(description = "Agreement-bot id.", - in = ParameterIn.PATH, - name = "node", - required = true), - new Parameter(description = "Message id.", - in = ParameterIn.PATH, - name = "msgid", - required = true), - new Parameter(description = "Organization id.", - in = ParameterIn.PATH, - name = "organization", - required = true)), - responses = Array(new responses.ApiResponse(content = Array(new Content(mediaType = "application/json", schema = new Schema(implementation = classOf[GetAgbotMsgsResponse]))), - description = "response body", - responseCode = "200"), - new responses.ApiResponse(description = "bad input", - responseCode = "400"), - new responses.ApiResponse(description = "invalid credentials", - responseCode = "401"), - new responses.ApiResponse(description = "access denied", - responseCode = "403"), - new responses.ApiResponse(description = "not found", - responseCode = "404")), - summary = "Returns A specific message that has been sent to this agreement-bot.") - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def agbotGetMsgRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "msgs" / Segment) & get) { - (orgid, id, msgid) => - val compositeId = OrgAndId(orgid, id).toString - logger.debug("msgid.toInt: " + msgid.toInt) - - exchAuth(TAgbot(compositeId), Access.READ) { - _ => - complete({ - db.run( - AgbotMsgsTQ.getMsg(agbotId = compositeId, - msgId = msgid.toInt) - .result - .map( - result => - GetAgbotMsgsResponse(lastIndex = 0, - messages = result.map( - message => - AgbotMsg(message = message.message, - msgId = message.msgId, - nodeId = message.nodeId, - nodePubKey = message.nodePubKey, - timeExpires = message.timeExpires, - timeSent = message.timeSent)).toList)) - .asTry - ) - .map({ - case Success(message) => - if(message.messages.nonEmpty) - (HttpCode.OK, message) - else - (HttpCode.NOT_FOUND, ApiResponse(ApiRespType.NOT_FOUND, ExchMsg.translate("not.found"))) - case Failure(t) => - (HttpCode.BAD_INPUT, ApiResponse(ApiRespType.BAD_INPUT, ExchMsg.translate("invalid.input.message", t.getMessage))) - }) - }) // end of complete - } // end of exchAuth - } - - // =========== DELETE /orgs/{organization}/agbots/{agreementbot}/msgs/{msgid} =============================== - @DELETE - @Path("{id}/msgs/{msgid}") - @Operation(summary = "Deletes a msg of an agbot", description = "Deletes a message that was sent to an agbot. This should be done by the agbot after each msg is read. Can be run by the owning user or the agbot.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id."), - new Parameter(name = "id", in = ParameterIn.PATH, description = "ID of the agbot."), - new Parameter(name = "msgid", in = ParameterIn.PATH, description = "ID of the msg to be deleted.")), - responses = Array( - new responses.ApiResponse(responseCode = "204", description = "deleted"), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def agbotDeleteMsgRoute: Route = (path("orgs" / Segment / "agbots" / Segment / "msgs" / Segment) & delete) { (orgid, id, msgIdStr) => - val compositeId = OrgAndId(orgid,id).toString - exchAuth(TAgbot(compositeId), Access.WRITE) { _ => - complete({ - try { - val msgId = msgIdStr.toInt // this can throw an exception, that's why this whole section is in a try/catch - db.run(AgbotMsgsTQ.getMsg(compositeId,msgId).delete.asTry).map({ - case Success(v) => - logger.debug("DELETE /agbots/" + id + "/msgs/" + msgId + " updated in changes table: " + v) - (HttpCode.DELETED, ApiResponse(ApiRespType.OK, ExchMsg.translate("agbot.message.deleted"))) - case Failure(t: DBProcessingError) => - t.toComplete - case Failure(t: org.postgresql.util.PSQLException) => - ExchangePosgtresErrorHandling.ioProblemError(t, ExchMsg.translate("agbot.message.not.deleted", msgId, compositeId, t.toString)) - case Failure(t) => - (HttpCode.INTERNAL_ERROR, ApiResponse(ApiRespType.INTERNAL_ERROR, ExchMsg.translate("agbot.message.not.deleted", msgId, compositeId, t.toString))) - }) - } catch { case e: Exception => (HttpCode.BAD_INPUT, ApiResponse(ApiRespType.BAD_INPUT, ExchMsg.translate("msgid.must.be.int", e))) } // the specific exception is NumberFormatException - }) // end of complete - } // end of exchAuth - } - */ - -} diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreement.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreement.scala index 73b75ed9..27046d2b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreement.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreement.scala @@ -45,10 +45,10 @@ trait Agreement extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def deleteAgreement(agreement: String, - agreementBot: String, - organization: String, - resource: String): Route = + def deleteAgreement(@Parameter(hidden = true) agreement: String, + @Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ db.run(AgbotAgreementsTQ.getAgreement(resource, agreement) @@ -113,10 +113,10 @@ trait Agreement extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def getAgreement(agreement: String, - agreementBot: String, - organization: String, - resource: String): Route = + def getAgreement(@Parameter(hidden = true) agreement: String, + @Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(AgbotAgreementsTQ.getAgreement(resource, agreement).result) .map({ @@ -176,10 +176,10 @@ trait Agreement extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def putAgreement(agreement: String, - agreementBot: String, - organization: String, - resource: String):Route = + def putAgreement(@Parameter(hidden = true) agreement: String, + @Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String):Route = put { entity(as[PutAgbotAgreementRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBot.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBot.scala index 889fa09b..c72116d2 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBot.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBot.scala @@ -64,10 +64,10 @@ trait AgreementBot extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def getAgreementBot(agreementBot: String, - identity: Identity, - organization: String, - resource: String): Route = + def getAgreementBot(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = parameter("attribute".?) { attribute => logger.debug(s"Doing GET /orgs/$organization/agbots/$agreementBot") @@ -136,10 +136,10 @@ trait AgreementBot extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def putAgreementBot(agreementBot: String, - identity: Identity, - organization: String, - resource: String): Route = + def putAgreementBot(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PutAgbotsRequest]) { reqBody => @@ -225,9 +225,9 @@ trait AgreementBot extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def patchAgreementBot(agreementBot: String, - organization: String, - resource: String): Route = + def patchAgreementBot(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = patch { entity(as[PatchAgbotsRequest]) { reqBody => @@ -286,9 +286,9 @@ trait AgreementBot extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def deleteAgreementBot(agreementBot: String, - organization: String, - resource: String): Route = + def deleteAgreementBot(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/agbots/$agreementBot") complete({ // remove does *not* throw an exception if the key does not exist diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBots.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBots.scala index d6c9701e..82514622 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBots.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/AgreementBots.scala @@ -58,8 +58,8 @@ trait AgreementBots extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def getAgreementBots(identity: Identity, - organization: String): Route = { + def getAgreementBots(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { parameter("idfilter".?, "name".?, "owner".?) { (idfilter, name, owner) => logger.debug(s"Doing GET /orgs/$organization/agbots") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreements.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreements.scala index fba4fce1..fbda2d79 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreements.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Agreements.scala @@ -44,9 +44,9 @@ trait Agreements extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def deleteAgreements(id: String, - orgid: String, - compositeId: String): Route = { + def deleteAgreements(@Parameter(hidden = true) id: String, + @Parameter(hidden = true) orgid: String, + @Parameter(hidden = true) compositeId: String): Route = { complete({ // remove does *not* throw an exception if the key does not exist db.run(AgbotAgreementsTQ.getAgreements(compositeId) @@ -112,9 +112,9 @@ trait Agreements extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/agreement") - def getAgreements(agreementBot: String, - organization: String, - resource: String): Route = { + def getAgreements(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotAgreementsTQ.getAgreements(resource).result) .map({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPattern.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPattern.scala index d287749c..733a97ff 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPattern.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPattern.scala @@ -47,10 +47,10 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment pattern") - private def deleteDeploymentPattern(agreementBot: String, - deploymentPattern: String, - organization: String, - resource: String): Route = { + private def deleteDeploymentPattern(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotPatternsTQ.getPattern(resource, deploymentPattern) .delete @@ -107,10 +107,10 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment pattern") - private def getDeploymentPattern(agreementBot: String, - deploymentPattern: String, - organization: String, - resource: String): Route = { + private def getDeploymentPattern(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotPatternsTQ.getPattern(resource, deploymentPattern).result).map({ list => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPatterns.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPatterns.scala index eec3f0d0..169416e1 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPatterns.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPatterns.scala @@ -46,9 +46,9 @@ trait DeploymentPatterns extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment pattern") - private def deleteDeploymentPatterns(agreementBot: String, - organization: String, - resource: String): Route = + private def deleteDeploymentPatterns(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ // remove does *not* throw an exception if the key does not exist db.run(AgbotPatternsTQ.getPatterns(resource) @@ -113,9 +113,9 @@ trait DeploymentPatterns extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment pattern") - private def getDeploymentPatterns(agreementBot: String, - organization: String, - resource: String): Route = { + private def getDeploymentPatterns(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotPatternsTQ.getPatterns(resource).result) .map({ @@ -158,9 +158,9 @@ trait DeploymentPatterns extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment pattern") - private def postDeploymentPatterns(agreementBot: String, - organization: String, - resource: String): Route = + private def postDeploymentPatterns(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = post { entity(as[PostAgbotPatternRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicies.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicies.scala index 42b8b35d..519e5c2d 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicies.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicies.scala @@ -46,9 +46,9 @@ trait DeploymentPolicies extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment policy") - private def deleteDeploymentPolicies(agreementBot: String, - organization: String, - resource: String): Route = + private def deleteDeploymentPolicies(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ // remove does *not* throw an exception if the key does not exist @@ -109,9 +109,9 @@ trait DeploymentPolicies extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment policy") - private def getDeploymentPolicies(agreementBot: String, - organization: String, - resource: String): Route = { + private def getDeploymentPolicies(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotBusinessPolsTQ.getBusinessPols(resource).result) .map({ @@ -165,9 +165,9 @@ trait DeploymentPolicies extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment policy") - private def postDeploymentPolicies(agreementBot: String, - organization:String, - resource: String): Route = + private def postDeploymentPolicies(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization:String, + @Parameter(hidden = true) resource: String): Route = post { entity(as[PostAgbotBusinessPolRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicy.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicy.scala index d7cfb1f0..62078b12 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicy.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/DeploymentPolicy.scala @@ -45,10 +45,10 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment policy") - private def deleteDeploymentPolicy(agreementBot: String, - deploymentPolicy: String, - organization: String, - resource: String): Route = { + private def deleteDeploymentPolicy(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotBusinessPolsTQ.getBusinessPol(resource, deploymentPolicy) .delete @@ -106,10 +106,10 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/deployment policy") - private def getDeploymentPolicy(agreementBot: String, - deploymentPolicy: String, - organization: String, - resource: String): Route = { + private def getDeploymentPolicy(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(AgbotBusinessPolsTQ.getBusinessPol(resource, deploymentPolicy).result) .map({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Heartbeat.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Heartbeat.scala index b6550944..ba9a5101 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Heartbeat.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Heartbeat.scala @@ -42,9 +42,9 @@ trait Heartbeat extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot") - def postHeartbeat(agreementBot: String, - organization: String, - resource: String): Route = { + def postHeartbeat(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { logger.debug(s"Doing POST /orgs/$organization/users/$agreementBot/heartbeat") complete({ db.run(AgbotsTQ.getLastHeartbeat(resource).update(ApiTime.nowUTC).asTry) diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Message.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Message.scala index 1eae875d..8747ca41 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Message.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Message.scala @@ -58,10 +58,10 @@ trait Message extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def deleteMessage(agreementBot: String, - msgId: Int, - organization: String, - resource: String): Route = + def deleteMessage(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) msgId: Int, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ //try { //val msgId = msgIdStr.toInt // this can throw an exception, that's why this whole section is in a try/catch @@ -115,10 +115,10 @@ trait Message extends JacksonSupport with AuthenticationSupport { responseCode = "404")), summary = "Returns A specific Message that has been sent to this Agreement Bot (AgBot).") @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def getMessage(agreementBot: String, - message: Int, - organization: String, - resource: String): Route = + def getMessage(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) message: Int, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(AgbotMsgsTQ.getMsg(agbotId = resource, msgId = message) diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Messages.scala b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Messages.scala index 9fe6919d..98bb2cb8 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Messages.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/agreementbot/Messages.scala @@ -53,9 +53,9 @@ trait Messages extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def getMessages(agreementBot: String, - organization: String, - resource: String): Route = + def getMessages(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = parameter("maxmsgs".as[Int].?) { maxMsgs => complete({ @@ -115,10 +115,10 @@ trait Messages extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "agreement bot/message") - def postMessages(agreementBot: String, - identity: Identity, - organization: String, - resource: String): Route = + def postMessages(@Parameter(hidden = true) agreementBot: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = entity (as[PostAgbotsMsgsRequest]) { reqBody => complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationDeploymentPatterns.scala b/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationDeploymentPatterns.scala index e78eb645..4078d29b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationDeploymentPatterns.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationDeploymentPatterns.scala @@ -118,8 +118,8 @@ trait OrganizationDeploymentPatterns extends JacksonSupport with AuthenticationS new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getOrganizationDeploymentPatterns(identity: Identity, - organization: String): Route = + def getOrganizationDeploymentPatterns(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = parameter("idfilter".?, "owner".?, "public".?, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationServices.scala b/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationServices.scala index af4b2f59..f9b09d7c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationServices.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/catalog/OrganizationServices.scala @@ -135,8 +135,8 @@ trait OrganizationServices extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getOrganizationServices(identity: Identity, - organization: String): Route = + def getOrganizationServices(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = parameter("owner".?, "public".?, "url".?, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPattern.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPattern.scala index 37414b0b..c33b135a 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPattern.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPattern.scala @@ -44,9 +44,9 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteDeploymentPattern(deploymentPattern: String, - organization: String, - resource: String): Route = + def deleteDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/patterns/$deploymentPattern") complete({ @@ -171,9 +171,9 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getDeploymentPattern(deploymentPattern: String, - organization: String, - resource: String): Route = + def getDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = parameter("attribute".?) { attribute => complete({ @@ -305,9 +305,9 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def patchDeploymentPattern(deploymentPattern: String, - organization: String, - resource: String): Route = + def patchDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = patch { entity(as[PatchPatternRequest]) { reqBody => @@ -539,10 +539,10 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { ) ) ) - def postDeploymentPattern(deploymentPattern: String, - identity: Identity, - organization: String, - resource: String): Route = + def postDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = entity(as[PostPutPatternRequest]) { reqBody => validateWithMsg(reqBody.getAnyProblem) { @@ -717,10 +717,10 @@ trait DeploymentPattern extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putDeploymentPattern(deploymentPattern: String, - identity: Identity, - organization: String, - resource: String): Route = + def putDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PostPutPatternRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPatterns.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPatterns.scala index bffc2aea..eb1524a0 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPatterns.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/DeploymentPatterns.scala @@ -131,8 +131,8 @@ trait DeploymentPatterns extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getDeploymentPatterns(identity: Identity, - organization: String): Route = + def getDeploymentPatterns(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = parameter("idfilter".?, "owner".?, "public".?, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/NodeHealth.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/NodeHealth.scala index 8c951c9b..a0e7bc45 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/NodeHealth.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/NodeHealth.scala @@ -105,8 +105,8 @@ trait NodeHealth extends JacksonSupport with AuthenticationSupport { ) ) ) - def postNodeHealth(deploymentPattern: String, - organization: String): Route = + def postNodeHealth(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String): Route = entity(as[PostNodeHealthRequest]) { reqBody => validateWithMsg(reqBody.getAnyProblem) { diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/Search.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/Search.scala index 38fcb05c..2d64c99c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/Search.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/Search.scala @@ -91,8 +91,8 @@ trait Search extends JacksonSupport with AuthenticationSupport { ) ) ) - def postSearchNode(organization: String, - resource: String): Route = + def postSearchNode(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = entity(as[PostPatternSearchRequest]) { reqBody => validateWithMsg(if(!(reqBody.secondsStale.isEmpty || !(reqBody.secondsStale.get < 0)) && !reqBody.serviceUrl.isEmpty) Some(ExchMsg.translate("bad.input")) else None) { diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Key.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Key.scala index 1cc21a06..c19a7f67 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Key.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Key.scala @@ -43,10 +43,10 @@ trait Key extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteKeyDeploymentPattern(pattern: String, - keyId: String, - orgid: String, - compositeId: String): Route = + def deleteKeyDeploymentPattern(@Parameter(hidden = true) pattern: String, + @Parameter(hidden = true) keyId: String, + @Parameter(hidden = true) orgid: String, + @Parameter(hidden = true) compositeId: String): Route = delete { complete({ var storedPublicField = false @@ -97,10 +97,10 @@ trait Key extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getKeyDeploymentPattern(pattern: String, - keyId: String, - orgid: String, - compositeId: String): Route = + def getKeyDeploymentPattern(@Parameter(hidden = true) pattern: String, + @Parameter(hidden = true) keyId: String, + @Parameter(hidden = true) orgid: String, + @Parameter(hidden = true) compositeId: String): Route = { complete({ db.run(PatternKeysTQ.getKey(compositeId, keyId).result).map({ list => @@ -144,10 +144,10 @@ trait Key extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putKeyDeploymentPattern(pattern: String, - keyId: String, - orgid: String, - compositeId: String): Route = + def putKeyDeploymentPattern(@Parameter(hidden = true) pattern: String, + @Parameter(hidden = true) keyId: String, + @Parameter(hidden = true) orgid: String, + @Parameter(hidden = true) compositeId: String): Route = put { extractRawBodyAsStr { reqBodyAsStr => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Keys.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Keys.scala index adfe678c..b60eed27 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Keys.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpattern/key/Keys.scala @@ -43,9 +43,9 @@ trait Keys extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "deployment pattern/key") - def deleteKeysDeploymentPattern(deploymentPattern: String, - organization: String, - resource: String): Route = + def deleteKeysDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ var storedPublicField = false @@ -97,9 +97,9 @@ trait Keys extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "deployment pattern/key") - def getKeysDeploymentPattern(deploymentPattern: String, - organization: String, - resource: String): Route = + def getKeysDeploymentPattern(@Parameter(hidden = true) deploymentPattern: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { complete({ db.run(PatternKeysTQ.getKeys(resource).result).map({ list => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicies.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicies.scala index 6fb799dc..5bf52a66 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicies.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicies.scala @@ -92,8 +92,8 @@ trait DeploymentPolicies extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getDeploymentPolicies(ident: Identity, - orgid: String): Route = + def getDeploymentPolicies(@Parameter(hidden = true) ident: Identity, + @Parameter(hidden = true) orgid: String): Route = parameter("idfilter".?, "owner".?, "label".?, "description".?) { (idfilter, owner, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicy.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicy.scala index da52a0e6..e5d8fe2a 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicy.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicy.scala @@ -50,9 +50,9 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteDeploymentPolicy(deploymentPolicy: String, - organization: String, - resource: String): Route = + def deleteDeploymentPolicy(@Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/business/policies/$deploymentPolicy") complete({ @@ -146,9 +146,9 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getDeploymentPolicy(deploymentPolicy: String, - organization: String, - resource: String): Route = + def getDeploymentPolicy(@Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = parameter("attribute".?) { attribute => complete({ @@ -266,9 +266,9 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def patchDeploymentPolicy(deploymentPolicy: String, - organization: String, - resource: String): Route = + def patchDeploymentPolicy(@Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = patch { entity(as[PatchBusinessPolicyRequest]) { reqBody => @@ -451,10 +451,10 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { ) ) ) - def postDeploymentPolicy(deploymentPolicy: String, - identity: Identity, - organization: String, - resource: String): Route = + def postDeploymentPolicy(@Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = post { entity(as[PostPutBusinessPolicyRequest]) { reqBody => @@ -608,10 +608,10 @@ trait DeploymentPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putDeploymentPolicy(deploymentPolicy: String, - identity: Identity, - organization: String, - resource: String): Route = + def putDeploymentPolicy(@Parameter(hidden = true) deploymentPolicy: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PostPutBusinessPolicyRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicySearch.scala b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicySearch.scala index 5cffbcc0..8035796c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicySearch.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/deploymentpolicy/DeploymentPolicySearch.scala @@ -100,10 +100,10 @@ trait DeploymentPolicySearch extends JacksonSupport with AuthenticationSupport { ) ) @io.swagger.v3.oas.annotations.tags.Tag(name = "deployment policy") - def postDeploymentPolicySearch(identity: Identity, - organization: String, - resource: String, - reqBody: PostBusinessPolicySearchRequest): Route = + def postDeploymentPolicySearch(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) reqBody: PostBusinessPolicySearchRequest): Route = complete({ implicit val formats: DefaultFormats.type = DefaultFormats val nodeOrgids: Set[String] = reqBody.nodeOrgids.getOrElse(List(organization)).toSet diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicies.scala b/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicies.scala index 5dc36abe..31917b71 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicies.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicies.scala @@ -94,7 +94,7 @@ trait ManagementPolicies extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getManagementPolicies(organization: String): Route = + def getManagementPolicies(@Parameter(hidden = true) organization: String): Route = parameter("idfilter".?, "owner".?, "label".?, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicy.scala b/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicy.scala index 4c5aea72..77489d66 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicy.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/managementpolicy/ManagementPolicy.scala @@ -79,9 +79,9 @@ trait ManagementPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getManagementPolicy(managementPolicy: String, - organization: String, - resource: String): Route = + def getManagementPolicy(@Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = get { parameter("attribute".?) { attribute => @@ -189,10 +189,10 @@ trait ManagementPolicy extends JacksonSupport with AuthenticationSupport { ) ) ) - def postManagementPolicy(ident: Identity, - managementPolicy: String, - organization: String, - resource: String): Route = + def postManagementPolicy(@Parameter(hidden = true) ident: Identity, + @Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = post { entity(as[PostPutManagementPolicyRequest]) { reqBody => @@ -312,10 +312,10 @@ trait ManagementPolicy extends JacksonSupport with AuthenticationSupport { ) ) ) - def putManagementPolicy(ident: Identity, - managementPolicy: String, - organization: String, - resource: String): Route = + def putManagementPolicy(@Parameter(hidden = true) ident: Identity, + @Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PostPutManagementPolicyRequest]) { reqBody => @@ -358,9 +358,9 @@ trait ManagementPolicy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteManagementPolicy(managementPolicy: String, - organization: String, - resource: String): Route = + def deleteManagementPolicy(@Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/managementpolicies/$managementPolicy") complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/ConfigurationState.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/ConfigurationState.scala index 1d7ea2bd..b69f322a 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/ConfigurationState.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/ConfigurationState.scala @@ -95,9 +95,9 @@ trait ConfigurationState extends JacksonSupport with AuthenticationSupport { ) ) ) - def postConfigurationState(node: String, - organization: String, - resource: String): Route = + def postConfigurationState(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = entity(as[PostNodeConfigStateRequest]) { reqBody => validateWithMsg(reqBody.getAnyProblem) { complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Details.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Details.scala index 65645684..b00dd41e 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Details.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Details.scala @@ -188,8 +188,8 @@ trait Details extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "access denied", responseCode = "403"), new responses.ApiResponse(description = "not found", responseCode = "404"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "node") - def getDetails(identity: Identity, - organization: String): Route = + def getDetails(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = parameter("arch".?, "id".?, "name".?, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Errors.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Errors.scala index 9353bdae..20152947 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Errors.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Errors.scala @@ -10,7 +10,9 @@ import org.apache.pekko.actor.ActorSystem import org.apache.pekko.event.LoggingAdapter import org.apache.pekko.http.scaladsl.server.Directives.{as, complete, delete, entity, get, path, put, _} import org.apache.pekko.http.scaladsl.server.Route +import org.json4s.JValue import org.openhorizon.exchangeapi.auth.{Access, AuthenticationSupport, DBProcessingError, OrgAndId, TNode} +import org.openhorizon.exchangeapi.route.node.PutNodeErrorRequest import org.openhorizon.exchangeapi.route.search.NodeError import org.openhorizon.exchangeapi.table.node.error.NodeErrorTQ import org.openhorizon.exchangeapi.table.resourcechange.{ResChangeCategory, ResChangeOperation, ResChangeResource, ResourceChange} @@ -42,9 +44,9 @@ trait Errors extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteErrors(node: String, - organization: String, - resource: String): Route = + def deleteErrors(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ db.run(NodeErrorTQ.getNodeError(resource).delete.asTry.flatMap({ @@ -84,9 +86,9 @@ trait Errors extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getErrors(node: String, - organization: String, - resource: String): Route = + def getErrors(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(NodeErrorTQ.getNodeError(resource).result).map({ list => @@ -134,7 +136,7 @@ trait Errors extends JacksonSupport with AuthenticationSupport { ) ), mediaType = "application/json", - schema = new Schema(implementation = classOf[PutNodeErrorRequest]) + schema = new Schema(implementation = classOf[List[String]]) ) ), required = true @@ -161,9 +163,9 @@ trait Errors extends JacksonSupport with AuthenticationSupport { ) ) ) - def putErrors(node: String, - organization: String, - resource: String): Route = + def putErrors(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PutNodeErrorRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Heartbeat.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Heartbeat.scala index e987e1b1..1272fc9f 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Heartbeat.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Heartbeat.scala @@ -40,9 +40,9 @@ trait Heartbeat extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def postHeartbeatNode(node: String, - organization: String, - resource:String): Route = + def postHeartbeatNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource:String): Route = { logger.debug(s"Doing POST /orgs/$organization/users/$node/heartbeat") complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Node.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Node.scala index 3c4b3db3..bcaa483c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Node.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Node.scala @@ -59,9 +59,9 @@ trait Node extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "node") - def deleteNode(node: String, - organization: String, - resource: String): Route = + def deleteNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/nodes/$node") val compositeId: String = OrgAndId(organization, node).toString @@ -194,7 +194,9 @@ trait Node extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "node") - def getNode(node: String, organization: String, resource: String): Route = + def getNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = get { parameter ("attribute".?) { attribute => @@ -407,9 +409,9 @@ trait Node extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "node") def patchNode(//identity: Identity, - node: String, - organization: String, - resource: String): Route = + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = patch { entity(as[PatchNodesRequest]) { reqBody => @@ -811,9 +813,9 @@ trait Node extends JacksonSupport with AuthenticationSupport { ) ) @io.swagger.v3.oas.annotations.tags.Tag(name = "node") - def putNode(node: String, - organization: String, - resource: String): Route = + def putNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { parameter ("noheartbeat".?) { noheartbeat => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/NodeDetails.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/NodeDetails.scala index 2ae28c9e..92b54590 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/NodeDetails.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/NodeDetails.scala @@ -1,5 +1,7 @@ package org.openhorizon.exchangeapi.route.node +import io.swagger.v3.oas.annotations.media.{ArraySchema, Schema} +import io.swagger.v3.oas.annotations.media.Schema.RequiredMode import org.openhorizon.exchangeapi.table.deploymentpattern.OneUserInputService import org.openhorizon.exchangeapi.table.node.{NodeHeartbeatIntervals, OneService, RegService} import org.openhorizon.exchangeapi.table.service.OneProperty @@ -8,7 +10,7 @@ import org.openhorizon.exchangeapi.utility.StrConstants case class NodeDetails(arch: Option[String] = None, connectivity: Option[Map[String, Boolean]] = None, constraints: Option[List[String]] = None, - errors: Option[List[Any]] = None, + @ArraySchema(schema = new Schema(implementation = classOf[Object])) errors: Option[List[Any]] = None, heartbeatIntervals: Option[NodeHeartbeatIntervals] = None, id: String = "", lastHeartbeat: Option[String] = None, diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Nodes.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Nodes.scala index 6ee6e215..8e1f60b8 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Nodes.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Nodes.scala @@ -130,7 +130,7 @@ trait Nodes extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "node") - def getNodes(organization: String): Route = + def getNodes(@Parameter(hidden = true) organization: String): Route = get { parameter("idfilter".?, "name".?, @@ -148,47 +148,6 @@ trait Nodes extends JacksonSupport with AuthenticationSupport { logger.debug(s"GET /orgs/$organization/nodes identity: ${ident.creds.id}") // can't display the whole ident object, because that contains the pw/token implicit val jsonFormats: Formats = DefaultFormats - /*var q = NodesTQ.getAllNodes(organization) - - arch.foreach(arch => { - if (arch.contains("%")) q = q.filter(_.arch like arch) else q = q.filter(_.arch === arch) - }) - - clusterNamespace.foreach(namespace => { - if (namespace.contains("%")) q = q.filter(_.clusterNamespace like namespace) else q = q.filter(_.clusterNamespace === namespace) - }) - - idfilter.foreach(id => { - if (id.contains("%")) q = q.filter(_.id like id) else q = q.filter(_.id === id) - }) - - if (isNamespaceScoped.isDefined) - q = q.filter(_.isNamespaceScoped === isNamespaceScoped.get) - - name.foreach(name => { - if (name.contains("%")) q = q.filter(_.name like name) else q = q.filter(_.name === name) - }) - - if (ident.isAdmin || ident.role.equals(AuthRoles.Agbot)) { - owner.foreach(owner => { - if (owner.contains("%")) q = q.filter(_.owner like owner) else q = q.filter(_.owner === owner) - }) - } else q = q.filter(_.owner === ident.identityString) - - owner.foreach(owner => { - if (owner.contains("%")) q = q.filter(_.owner like owner) else q = q.filter(_.owner === owner) - }) - - - if (nodetype.isDefined) { - val nt: String = nodetype.get.toLowerCase - if (NodeType.isDevice(nt)) q = q.filter(r => { - r.nodeType === nt || r.nodeType === "" - }) else if (NodeType.isCluster(nt)) q = q.filter(_.nodeType === nt) - } - - val combinedQuery = for {((nodes, _), groups) <- (q joinLeft NodeGroupAssignmentTQ on (_.id === _.node)).joinLeft(NodeGroupTQ).on(_._2.map(_.group) === _.group)} yield (nodes, groups.map(_.name)) */ - val nodes = NodesTQ.filter(_.orgid === organization) .filterOpt(arch)((node, arch) => node.arch like arch) diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Policy.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Policy.scala index f9080acc..e48b4db7 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Policy.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Policy.scala @@ -43,9 +43,9 @@ trait Policy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deletePolicyNode(node: String, - organization: String, - resource: String): Route = + def deletePolicyNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ db.run(NodePolicyTQ.getNodePolicy(resource).delete.asTry.flatMap({ @@ -90,9 +90,9 @@ trait Policy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getPolicyNode(node: String, - organization: String, - resource: String): Route = + def getPolicyNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(NodePolicyTQ.getNodePolicy(resource).result).map({ list => logger.debug("GET /orgs/"+organization+"/nodes/"+node+"/policy result size: "+list.size) @@ -197,9 +197,9 @@ trait Policy extends JacksonSupport with AuthenticationSupport { ) ) ) - def putPolicyNode(node: String, - organization: String, - resource: String): Route = + def putPolicyNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { parameter("noheartbeat".?) { noheartbeat => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeErrorRequest.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeErrorRequest.scala index 34e2dfbb..90b243a1 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeErrorRequest.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeErrorRequest.scala @@ -1,12 +1,13 @@ package org.openhorizon.exchangeapi.route.node +import io.swagger.v3.oas.annotations.media.{ArraySchema, Schema} import org.json4s.jackson.Serialization.write import org.json4s.{DefaultFormats, Formats} import org.openhorizon.exchangeapi.table.node.error.NodeErrorRow import org.openhorizon.exchangeapi.utility.ApiTime /** Input body for PUT /orgs/{organization}/nodes/{node}/errors */ -final case class PutNodeErrorRequest(errors: List[Any]) { +final case class PutNodeErrorRequest(@ArraySchema(schema = new Schema(implementation = classOf[Object])) errors: List[Any]) { require(errors!=null) protected implicit val jsonFormats: Formats = DefaultFormats def getAnyProblem: Option[String] = None diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeStatusRequest.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeStatusRequest.scala index 942f7d7e..78390d4d 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeStatusRequest.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/PutNodeStatusRequest.scala @@ -1,5 +1,6 @@ package org.openhorizon.exchangeapi.route.node +import io.swagger.v3.oas.annotations.media.{ArraySchema, Schema} import org.json4s.jackson.Serialization.write import org.json4s.{DefaultFormats, Formats} import org.openhorizon.exchangeapi.table.node.OneService diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/Status.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/Status.scala index 1a721b78..dc76de2d 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/Status.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/Status.scala @@ -73,9 +73,9 @@ trait Status extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getStatusNode(node: String, - organization: String, - resource: String): Route = + def getStatusNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(NodeStatusTQ.getNodeStatus(resource).result).map({ list => @@ -165,9 +165,9 @@ trait Status extends JacksonSupport with AuthenticationSupport { ) ) ) - def putStatusNode(node: String, - organization: String, - resource: String): Route = + def putStatusNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PutNodeStatusRequest]) { reqBody => @@ -206,9 +206,9 @@ trait Status extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteStatusNode(node: String, - organization: String, - resource: String): Route = + def deleteStatusNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ db.run(NodeStatusTQ.getNodeStatus(resource).delete.asTry.flatMap({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreement.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreement.scala index d506aeb8..dee01bf9 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreement.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreement.scala @@ -73,10 +73,10 @@ trait Agreement extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getAgreementNode(agreement: String, - node: String, - organization: String, - resource: String): Route = + def getAgreementNode(@Parameter(hidden = true) agreement: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(NodeAgreementsTQ.getAgreement(resource, agreement).result).map({ list => logger.debug(s"GET /orgs/$organization/nodes/$node/agreements/$agreement result size: ${list.size}") @@ -160,10 +160,10 @@ trait Agreement extends JacksonSupport with AuthenticationSupport { ) ) ) - def putAgreementNode(agreement: String, - node: String, - organization: String, - resource: String): Route = + def putAgreementNode(@Parameter(hidden = true) agreement: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { parameter("noheartbeat".?) { noheartbeat => @@ -235,10 +235,10 @@ trait Agreement extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteAgreementNode(agreement: String, - node: String, - organization: String, - resource: String): Route = + def deleteAgreementNode(@Parameter(hidden = true) agreement: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { complete({ db.run(NodeAgreementsTQ.getAgreement(resource,agreement).delete.asTry.flatMap({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreements.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreements.scala index c28533eb..0f00b940 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreements.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/agreement/Agreements.scala @@ -43,9 +43,9 @@ trait Agreements extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteAgreementsNode(node: String, - organization: String, - resource: String): Route = + def deleteAgreementsNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ // remove does *not* throw an exception if the key does not exist db.run(NodeAgreementsTQ.getAgreements(resource).delete.asTry.flatMap({ @@ -117,9 +117,9 @@ trait Agreements extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getAgreementsNode(node: String, - organization: String, - resource: String): Route = + def getAgreementsNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run(NodeAgreementsTQ.getAgreements(resource).result).map({ list => logger.debug(s"GET /orgs/$organization/nodes/$node/agreements result size: ${list.size}") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Status.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Status.scala index cd0ab464..80f23a3b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Status.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Status.scala @@ -56,10 +56,10 @@ trait Status extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteStatusMangementPolicy(managementPolicy: String, - node: String, - organization: String, - resource: String): Route = + def deleteStatusMangementPolicy(@Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/nodes/$node/managementStatus/$managementPolicy") complete({ @@ -137,10 +137,10 @@ trait Status extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getStatusMangementPolicy(managementPolicy: String, - node: String, - organization: String, - resource: String): Route = + def getStatusMangementPolicy(@Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = { logger.debug(s"Doing GET /orgs/$organization/nodes/$node/managementStatus/$managementPolicy") complete({ @@ -228,10 +228,10 @@ trait Status extends JacksonSupport with AuthenticationSupport { ) ) ) - def putStatusManagementPolicy(managementPolicy: String, - node: String, - organization: String, - resource: String): Route = + def putStatusManagementPolicy(@Parameter(hidden = true) managementPolicy: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = put { entity(as[PutNodeMgmtPolStatusRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Statuses.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Statuses.scala index b6280039..bf870564 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Statuses.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/managementpolicy/Statuses.scala @@ -86,8 +86,8 @@ trait Statuses extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getStatusManagementPolicies(node: String, - organization: String): Route = + def getStatusManagementPolicies(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String): Route = complete({ var q = NodeMgmtPolStatuses.getNodeMgmtPolStatuses(organization + "/" + node).sortBy(_.policy.asc.nullsFirst) db.run(q.result).map({ list => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Message.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Message.scala index 74c205d6..04183973 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Message.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Message.scala @@ -56,8 +56,8 @@ trait Message extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "not found", responseCode = "404")), summary = "Returns A specific message that has been sent to this node.") - def getMessageNode(message: String, - resource: String): Route = + def getMessageNode(@Parameter(hidden = true) message: String, + @Parameter(hidden = true) resource: String): Route = complete({ db.run( NodeMsgsTQ.getMsg(nodeId = resource, @@ -99,9 +99,9 @@ trait Message extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteMessageNode(message: String, - node: String, - resource: String): Route = + def deleteMessageNode(@Parameter(hidden = true) message: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) resource: String): Route = complete({ try { val msgId: Int = message.toInt // this can throw an exception, that's why this whole section is in a try/catch diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Messages.scala b/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Messages.scala index 6f50d5cf..24688523 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Messages.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/node/message/Messages.scala @@ -50,9 +50,9 @@ trait Messages extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getMessagesNode(node: String, - organization: String, - resource: String): Route = + def getMessagesNode(@Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = parameter("maxmsgs".?) { maxmsgsStrOpt => validate(Try(maxmsgsStrOpt.map(_.toInt)).isSuccess, ExchMsg.translate("invalid.int.for.name", maxmsgsStrOpt.getOrElse(""), "maxmsgs")) { @@ -130,10 +130,10 @@ trait Messages extends JacksonSupport with AuthenticationSupport { ) ) ) - def postMessagesNode(identity: Identity, - node: String, - organization: String, - resource: String): Route = + def postMessagesNode(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = entity(as[PostNodesMsgsRequest]) { reqBody => complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroup.scala b/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroup.scala index 75c240a8..b043859a 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroup.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroup.scala @@ -58,10 +58,10 @@ trait NodeGroup extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "not found", responseCode = "404")), summary = "Deletes a Node Group") - def deleteNodeGroup(highAvailabilityGroup: String, - identity: Identity, - organization: String, - resource: String): Route = + def deleteNodeGroup(@Parameter(hidden = true) highAvailabilityGroup: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/hagroups/$highAvailabilityGroup") complete({ @@ -198,9 +198,9 @@ trait NodeGroup extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "not found", responseCode = "404")), summary = "Lists all members of the specified Node Group (HA Group)") - def getNodeGroup(highAvailabilityGroup: String, - identity: Identity, - organization: String): Route = + def getNodeGroup(@Parameter(hidden = true) highAvailabilityGroup: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { logger.debug(s"doing GET /orgs/$organization/hagroups") complete({ @@ -288,9 +288,9 @@ trait NodeGroup extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "node already belongs to other group", responseCode = "409")), summary = "Update the nodes that belong to an existing Node Group (HA Group)") - def putNodeGroup(highAvailabilityGroup: String, - identity: Identity, - organization: String): Route = + def putNodeGroup(@Parameter(hidden = true) highAvailabilityGroup: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { entity(as[PutNodeGroupsRequest]) { reqBody => @@ -502,10 +502,10 @@ trait NodeGroup extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "node already belongs to other group", responseCode = "409")), summary = "Insert the nodes that belong to an existing Node Group (HA Group)") - def postNodeGroup(highAvailabilityGroup: String, - identity: Identity, - organization: String, - resource: String): Route = + def postNodeGroup(@Parameter(hidden = true) highAvailabilityGroup: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = post { entity(as[PostPutNodeGroupsRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroups.scala b/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroups.scala index 02a30df1..28d01e06 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroups.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/NodeGroups.scala @@ -74,8 +74,8 @@ trait NodeGroups extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "not found", responseCode = "404")), summary = "Lists all members of all Node Groups (HA Groups)") - def getNodeGroups(identity: Identity, - organization: String): Route = + def getNodeGroups(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { logger.debug(s"doing GET /orgs/$organization/hagroups") complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/node/Node.scala b/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/node/Node.scala index 9ad5029e..b9d72a7f 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/node/Node.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/nodegroup/node/Node.scala @@ -56,10 +56,10 @@ trait Node extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "not found", responseCode = "404")), summary = "Deletes a Node from a Node Group") - def deleteNodeFromNodeGroup(highAvailabilityGroup: String, - node: String, - organization: String, - resource: String): Route = + def deleteNodeFromNodeGroup(@Parameter(hidden = true) highAvailabilityGroup: String, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/hagroups/$highAvailabilityGroup/nodes/$node") complete({ @@ -161,11 +161,11 @@ trait Node extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(description = "node already belongs to other group", responseCode = "409")), summary = "Insert a Node into a High Availablity Node Group") - def postNodeToNodeGroup(highAvailabilityGroup: String, - identity: Identity, - node: String, - organization: String, - resource: String): Route = + def postNodeToNodeGroup(@Parameter(hidden = true) highAvailabilityGroup: String, + @Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) node: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String): Route = post { logger.debug(s"Doing POST /orgs/$organization/hagroups/$highAvailabilityGroup/nodes/$node") complete({ diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Changes.scala b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Changes.scala index b593e9fb..efe7bd92 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Changes.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Changes.scala @@ -34,10 +34,10 @@ trait Changes extends JacksonSupport with AuthenticationSupport{ def logger: LoggingAdapter implicit def executionContext: ExecutionContext - def buildResourceChangesResponse(inputList: scala.Seq[ResourceChangeRow], - hitMaxRecords: Boolean, - inputChangeId: Long, - maxChangeIdOfTable: Long): ResourceChangesRespObject ={ + def buildResourceChangesResponse(@Parameter(hidden = true) inputList: scala.Seq[ResourceChangeRow], + @Parameter(hidden = true) hitMaxRecords: Boolean, + @Parameter(hidden = true) inputChangeId: Long, + @Parameter(hidden = true) maxChangeIdOfTable: Long): ResourceChangesRespObject ={ // Sort the rows based on the changeId. Default order is ascending, which is what we want logger.debug(s"POST /orgs/{organization}/changes sorting ${inputList.size} rows") // val inputList = inputListUnsorted.sortBy(_.changeId) // Note: we are doing the sorting here instead of in the db via sql, because the latter seems to use a lot of db cpu @@ -125,9 +125,9 @@ trait Changes extends JacksonSupport with AuthenticationSupport{ ) ) ) - def postChanges(identity: Identity, - organization: String, - reqBody: ResourceChangesRequest): Route = + def postChanges(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) reqBody: ResourceChangesRequest): Route = complete({ logger.debug(s"Doing POST /orgs/$organization/changes - identity: ${identity.identityString}") // make sure callers obey maxRecords cap set in config, defaults is 10,000 diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Cleanup.scala b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Cleanup.scala index 500b7c93..f1238e67 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Cleanup.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Cleanup.scala @@ -2,6 +2,7 @@ package org.openhorizon.exchangeapi.route.organization import com.github.pjfanning.pekkohttpjackson.JacksonSupport +import io.swagger.v3.oas.annotations.Parameter import jakarta.ws.rs.Path import org.apache.pekko.actor.ActorSystem import org.apache.pekko.event.LoggingAdapter @@ -34,7 +35,7 @@ trait Cleanup extends JacksonSupport with AuthenticationSupport { /* ====== DELETE /orgs//changes/cleanup ================================ */ // This route is just for unit testing as a way to clean up the changes table once testing has completed // Otherwise the changes table gets clogged with entries in the orgs from testing - def deleteChanges(organization: String): Route = + def deleteChanges(@Parameter(hidden = true) organization: String): Route = entity(as[DeleteOrgChangesRequest]) { reqBody => logger.debug(s"Doing POST /orgs/$organization/changes/cleanup") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/organization/OrgStatus.scala b/src/main/scala/org/openhorizon/exchangeapi/route/organization/OrgStatus.scala deleted file mode 100644 index 02e7ba3d..00000000 --- a/src/main/scala/org/openhorizon/exchangeapi/route/organization/OrgStatus.scala +++ /dev/null @@ -1,12 +0,0 @@ -package org.openhorizon.exchangeapi.route.organization - -class OrgStatus() { - var msg: String = "" - var numberOfUsers: Int = 0 - var numberOfNodes: Int = 0 - var numberOfNodeAgreements: Int = 0 - var numberOfRegisteredNodes: Int = 0 - var numberOfNodeMsgs: Int = 0 - var dbSchemaVersion: Int = 0 - def toGetOrgStatusResponse: GetOrgStatusResponse = GetOrgStatusResponse(msg, numberOfUsers, numberOfNodes, numberOfNodeAgreements,numberOfRegisteredNodes, numberOfNodeMsgs, dbSchemaVersion) -} diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organization.scala b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organization.scala index e420b03c..7a7e25cd 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organization.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organization.scala @@ -77,7 +77,7 @@ trait Organization extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getOrganization(organization: String): Route = + def getOrganization(@Parameter(hidden = true) organization: String): Route = { parameter("attribute".?) { attribute => @@ -176,7 +176,7 @@ trait Organization extends JacksonSupport with AuthenticationSupport { ) ) ) - def postOrganization(orgId: String): Route = + def postOrganization(@Parameter(hidden = true) orgId: String): Route = entity(as[PostPutOrgRequest]) { reqBody => logger.debug(s"Doing POST /orgs/$orgId") @@ -242,8 +242,8 @@ trait Organization extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putOrganization(organization: String, - reqBody: PostPutOrgRequest): Route = + def putOrganization(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) reqBody: PostPutOrgRequest): Route = { logger.debug(s"Doing PUT /orgs/$organization with orgId:$organization") @@ -313,8 +313,8 @@ trait Organization extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def patchOrganization(organization: String, - reqBody: PatchOrgRequest): Route = + def patchOrganization(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) reqBody: PatchOrgRequest): Route = { logger.debug(s"Doing PATCH /orgs/$organization with orgId:$organization") @@ -357,7 +357,7 @@ trait Organization extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteOrganization(organization: String): Route = + def deleteOrganization(@Parameter(hidden = true) organization: String): Route = { logger.debug(s"Doing DELETE /orgs/$organization") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organizations.scala b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organizations.scala index b7639584..a5c41958 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organizations.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Organizations.scala @@ -141,9 +141,9 @@ trait Organizations extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getOrganizations(ident: Identity, - label: Option[String], - orgType: Option[String]): Route = + def getOrganizations(@Parameter(hidden = true) ident: Identity, + @Parameter(hidden = true) label: Option[String], + @Parameter(hidden = true) orgType: Option[String]): Route = { logger.debug(s"Doing GET /orgs with orgType:$orgType, label:$label") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Status.scala b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Status.scala index a6e864fd..f62e2a3e 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/organization/Status.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/organization/Status.scala @@ -22,67 +22,13 @@ import slick.jdbc.PostgresProfile.api._ import scala.concurrent.ExecutionContext import scala.util.{Failure, Success} -@Path("/v1/orgs/{organization}/status") -@io.swagger.v3.oas.annotations.tags.Tag(name = "organization") trait Status extends JacksonSupport with AuthenticationSupport { def db: Database def system: ActorSystem def logger: LoggingAdapter implicit def executionContext: ExecutionContext - // ====== GET /orgs/{organization}/status ================================ - @GET - @Operation(summary = "Returns summary status of the org", description = "Returns the totals of key resources in the org. Can be run by any id in this org or a hub admin.", - parameters = Array( - new Parameter(name = "organization", in = ParameterIn.PATH, description = "Organization id.")), - responses = Array( - new responses.ApiResponse(responseCode = "200", description = "response body", - content = Array(new Content(schema = new Schema(implementation = classOf[GetOrgStatusResponse])))), - new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), - new responses.ApiResponse(responseCode = "403", description = "access denied"), - new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getStatus(ident: Identity, - orgId: String): Route = - { - logger.debug(s"GET /orgs/$orgId/status") - - complete({ - val statusResp = new OrgStatus() - //perf: use a DBIO.sequence instead. It does essentially the same thing, but more efficiently - db.run(UsersTQ.getAllUsers(orgId).length.result.asTry.flatMap({ - case Success(v) => statusResp.numberOfUsers = v - NodesTQ.getAllNodes(orgId).length.result.asTry - case Failure(t) => DBIO.failed(t).asTry - }).flatMap({ - case Success(v) => statusResp.numberOfNodes = v - NodeAgreementsTQ.getAgreementsWithState(orgId).length.result.asTry - case Failure(t) => DBIO.failed(t).asTry - }).flatMap({ - case Success(v) => statusResp.numberOfNodeAgreements = v - NodesTQ.getRegisteredNodesInOrg(orgId).length.result.asTry - case Failure(t) => DBIO.failed(t).asTry - }).flatMap({ - case Success(v) => statusResp.numberOfRegisteredNodes = v - NodeMsgsTQ.getNodeMsgsInOrg(orgId).length.result.asTry - case Failure(t) => DBIO.failed(t).asTry - }).flatMap({ - case Success(v) => statusResp.numberOfNodeMsgs = v - SchemaTQ.getSchemaVersion.result.asTry - case Failure(t) => DBIO.failed(t).asTry - })).map({ - case Success(v) => statusResp.dbSchemaVersion = v.head - statusResp.msg = ExchMsg.translate("exchange.server.operating.normally") - (HttpCode.OK, statusResp.toGetOrgStatusResponse) - case Failure(t: org.postgresql.util.PSQLException) => - if (t.getMessage.contains("An I/O error occurred while sending to the backend")) (HttpCode.BAD_GW, statusResp.toGetOrgStatusResponse) - else (HttpCode.INTERNAL_ERROR, statusResp.toGetOrgStatusResponse) - case Failure(t) => statusResp.msg = t.getMessage - (HttpCode.INTERNAL_ERROR, statusResp.toGetOrgStatusResponse) - }) - }) - } - - val statusOrganization: Route = + /*val statusOrganization: Route = path("orgs" / Segment /"status") { organization => get { @@ -91,5 +37,5 @@ trait Status extends JacksonSupport with AuthenticationSupport { getStatus(identity, organization) } } - } + }*/ } diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeError.scala b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeError.scala index 92e51b7b..6fa30a78 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeError.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeError.scala @@ -38,8 +38,8 @@ trait NodeError extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def postNodeErrorSearch(identity: Identity, - organization: String): Route = + def postNodeErrorSearch(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { logger.debug(s"Doing POST /orgs/$organization/search/nodes/error") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeErrors.scala b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeErrors.scala index 9f40552f..8f78704c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeErrors.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeErrors.scala @@ -39,8 +39,8 @@ trait NodeErrors extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getNodeErrorsSearch(identity: Identity, - organization: String): Route = + def getNodeErrorsSearch(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { logger.debug(s"Doing GET /orgs/$organization/search/nodes/error/all") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeHealth.scala b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeHealth.scala index a9785390..351d3a49 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeHealth.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeHealth.scala @@ -102,7 +102,7 @@ trait NodeHealth extends JacksonSupport with AuthenticationSupport { ) ) ) - def postNodeHealthSearch(orgid: String): Route = + def postNodeHealthSearch(@Parameter(hidden = true) orgid: String): Route = entity(as[PostNodeHealthRequest]) { reqBody => logger.debug(s"Doing POST /orgs/$orgid/search/nodehealth") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeService.scala b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeService.scala index 6f450f0f..cd9cbbba 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeService.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/search/NodeService.scala @@ -101,8 +101,8 @@ trait NodeService extends JacksonSupport with AuthenticationSupport { ) ) ) - def postNodeServiceSearch(identity: Identity, - organization: String): Route = + def postNodeServiceSearch(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = entity(as[PostServiceSearchRequest]) { reqBody => logger.debug(s"Doing POST /orgs/$organization/search/nodes/service") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/Policy.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/Policy.scala index 8402bee0..36a3e99c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/Policy.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/Policy.scala @@ -42,9 +42,9 @@ trait Policy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getPolicyService(organization: String, - resource: String, - service: String): Route = + def getPolicyService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = complete({ db.run(ServicePolicyTQ.getServicePolicy(resource).result).map({ list => @@ -122,9 +122,9 @@ trait Policy extends JacksonSupport with AuthenticationSupport { ) ) ) - def putPolicyService(organization: String, - resource: String, - service: String): Route = + def putPolicyService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = put { entity(as[PutServicePolicyRequest]) { reqBody => @@ -172,9 +172,9 @@ trait Policy extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deletePolicyService(organization: String, - resource: String, - service: String): Route = + def deletePolicyService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = delete { complete({ var storedPublicField = false diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/Service.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/Service.scala index 9de0092e..1da56823 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/Service.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/Service.scala @@ -133,9 +133,9 @@ trait Service extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getService(organization: String, - resource: String, - service: String): Route = + def getService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = get { parameter("attribute".?) { attribute => @@ -229,10 +229,10 @@ trait Service extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putService(identity: Identity, - organization: String, - resource: String, - service: String): Route = + def putService(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = put { entity(as[PostPutServiceRequest]) { reqBody => @@ -365,9 +365,9 @@ trait Service extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def patchService(organization: String, - resource: String, - service: String): Route = + def patchService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = patch { entity(as[PatchServiceRequest]) { reqBody => @@ -477,9 +477,9 @@ trait Service extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteService(organization: String, - resource: String, - service: String): Route = + def deleteService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/services/$service") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/Services.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/Services.scala index 4a0a9e53..54629374 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/Services.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/Services.scala @@ -149,8 +149,8 @@ trait Services extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getServices(identity: Identity, - organization: String): Route = + def getServices(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = parameter("owner".?, "public".?, "url".?, "version".?, "arch".?, "nodetype".?, "requiredurl".?) { (owner, public, url, version, arch, nodetype, requiredurl) => validateWithMsg(GetServicesUtils.getServicesProblem(public, version, nodetype)) { @@ -265,8 +265,8 @@ trait Services extends JacksonSupport with AuthenticationSupport { ) ) ) - def postServices(identity: Identity, - organization: String): Route = + def postServices(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = entity(as[PostPutServiceRequest]) { reqBody => validateWithMsg(reqBody.getAnyProblem(organization, null)) { diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuth.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuth.scala index 0c7f1204..5f791817 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuth.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuth.scala @@ -43,10 +43,10 @@ trait DockerAuth extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getDockerAuth(dockerAuth: String, - organization: String, - resource: String, - service: String): Route = + def getDockerAuth(@Parameter(hidden = true) dockerAuth: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = complete({ Try(dockerAuth.toInt) match { case Success(dockauthId) => @@ -76,10 +76,10 @@ trait DockerAuth extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putDockerAuth(dockerAuth: String, - organization: String, - resource: String, - service: String): Route = + def putDockerAuth(@Parameter(hidden = true) dockerAuth: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = put { entity(as[PostPutServiceDockAuthRequest]) { reqBody => @@ -133,10 +133,10 @@ trait DockerAuth extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteDockerAuth(dockerAuth: String, - organization: String, - resource: String, - service: String): Route = + def deleteDockerAuth(@Parameter(hidden = true) dockerAuth: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = delete { complete({ Try(dockerAuth.toInt) match { diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuths.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuths.scala index 309f4d00..ba60531b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuths.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/dockerauth/DockerAuths.scala @@ -41,9 +41,9 @@ trait DockerAuths extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteDockerAuths(organization: String, - resource: String, - service: String): Route = + def deleteDockerAuths(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = delete { complete({ var storedPublicField = false @@ -114,9 +114,9 @@ trait DockerAuths extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getDockerAuths(organization: String, - resource: String, - service: String): Route = + def getDockerAuths(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = complete({ db.run(ServiceDockAuthsTQ.getDockAuths(resource).result).map({ list => @@ -189,9 +189,9 @@ trait DockerAuths extends JacksonSupport with AuthenticationSupport { ) ) ) - def postDockerAuths(organization: String, - resource: String, - service: String): Route = + def postDockerAuths(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = post { entity(as[PostPutServiceDockAuthRequest]) { reqBody => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Key.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Key.scala index 72295809..6780e7b8 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Key.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Key.scala @@ -44,10 +44,10 @@ trait Key extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getKeyService(keyId: String, - orgid: String, - compositeId: String, - service: String): Route = + def getKeyService(@Parameter(hidden = true) keyId: String, + @Parameter(hidden = true) orgid: String, + @Parameter(hidden = true) compositeId: String, + @Parameter(hidden = true) service: String): Route = get { complete({ db.run(ServiceKeysTQ.getKey(compositeId, keyId).result).map({ list => @@ -86,10 +86,10 @@ trait Key extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putKeyService(keyId: String, - orgid: String, - compositeId: String, - service: String): Route = + def putKeyService(@Parameter(hidden = true) keyId: String, + @Parameter(hidden = true) orgid: String, + @Parameter(hidden = true) compositeId: String, + @Parameter(hidden = true) service: String): Route = put { extractRawBodyAsStr { reqBodyAsStr => @@ -139,10 +139,10 @@ trait Key extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteKeyService(key: String, - organization: String, - resource: String, - service: String): Route = + def deleteKeyService(@Parameter(hidden = true) key: String, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = delete { complete({ var storedPublicField = false diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Keys.scala b/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Keys.scala index fb83d6e0..ac1d8a84 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Keys.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/service/key/Keys.scala @@ -40,9 +40,9 @@ trait Keys extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) @io.swagger.v3.oas.annotations.tags.Tag(name = "service/key") - def deleteKeysService(organization: String, - resource: String, - service: String): Route = + def deleteKeysService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = delete { complete({ var storedPublicField = false @@ -107,9 +107,9 @@ trait Keys extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getKeysService(organization: String, - resource: String, - service: String): Route = + def getKeysService(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) service: String): Route = get { complete({ db.run(ServiceKeysTQ.getKeys(resource).result).map({ list => diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/user/ChangePassword.scala b/src/main/scala/org/openhorizon/exchangeapi/route/user/ChangePassword.scala index 756e6dad..20970ad8 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/user/ChangePassword.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/user/ChangePassword.scala @@ -82,9 +82,9 @@ trait ChangePassword extends JacksonSupport with AuthenticationSupport { ) ) ) - def postChangePassword(organization: String, - compositeId: String, - username: String): Route = + def postChangePassword(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) compositeId: String, + @Parameter(hidden = true) username: String): Route = entity(as[ChangePwRequest]) { reqBody => logger.debug(s"Doing POST /orgs/$organization/users/$username") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/user/Confirm.scala b/src/main/scala/org/openhorizon/exchangeapi/route/user/Confirm.scala index da392e46..4546d42c 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/user/Confirm.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/user/Confirm.scala @@ -33,8 +33,8 @@ trait Confirm extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def postConfirm(organization: String, - username: String): Route = + def postConfirm(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) username: String): Route = { logger.debug(s"Doing POST /orgs/$organization/users/$username/confirm") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/user/User.scala b/src/main/scala/org/openhorizon/exchangeapi/route/user/User.scala index c04fdb68..05094326 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/user/User.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/user/User.scala @@ -64,10 +64,10 @@ trait User extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getUser(identity: Identity, - organization: String, - resource: String, - username: String): Route = + def getUser(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) username: String): Route = get { logger.debug(s"Doing GET /orgs/$organization/users/$username") @@ -164,10 +164,10 @@ trait User extends JacksonSupport with AuthenticationSupport { ) ) ) - def postUser(identity: Identity, - organization: String, - resource: String, - username: String): Route = + def postUser(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) username: String): Route = post { entity(as[PostPutUsersRequest]) { reqBody => @@ -230,10 +230,10 @@ trait User extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def putUser(identity: Identity, - organization: String, - resource: String, - username: String): Route = + def putUser(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) username: String): Route = put { entity(as[PostPutUsersRequest]) { reqBody => @@ -295,10 +295,10 @@ trait User extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def patchUser(identity: Identity, - organization: String, - resource: String, - username: String): Route = + def patchUser(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) username: String): Route = patch { entity(as[PatchUsersRequest]) { reqBody => @@ -345,9 +345,9 @@ trait User extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def deleteUser(organization: String, - resource: String, - username: String): Route = + def deleteUser(@Parameter(hidden = true) organization: String, + @Parameter(hidden = true) resource: String, + @Parameter(hidden = true) username: String): Route = delete { logger.debug(s"Doing DELETE /orgs/$organization/users/$username") diff --git a/src/main/scala/org/openhorizon/exchangeapi/route/user/Users.scala b/src/main/scala/org/openhorizon/exchangeapi/route/user/Users.scala index fce97f20..41744c47 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/route/user/Users.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/route/user/Users.scala @@ -76,8 +76,8 @@ trait Users extends JacksonSupport with AuthenticationSupport { new responses.ApiResponse(responseCode = "401", description = "invalid credentials"), new responses.ApiResponse(responseCode = "403", description = "access denied"), new responses.ApiResponse(responseCode = "404", description = "not found"))) - def getUsers(identity: Identity, - organization: String): Route = + def getUsers(@Parameter(hidden = true) identity: Identity, + @Parameter(hidden = true) organization: String): Route = { logger.debug(s"Doing GET /orgs/$organization/users") diff --git a/src/main/scala/org/openhorizon/exchangeapi/table/node/OneService.scala b/src/main/scala/org/openhorizon/exchangeapi/table/node/OneService.scala index f0285cb9..4ce500ab 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/table/node/OneService.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/table/node/OneService.scala @@ -1,10 +1,12 @@ package org.openhorizon.exchangeapi.table.node +import io.swagger.v3.oas.annotations.media.Schema + final case class OneService(agreementId: String, serviceUrl: String, orgid: String, version: String, arch: String, containerStatus: List[ContainerStatus], - operatorStatus: Option[Any], + @Schema(implementation = classOf[Object]) operatorStatus: Option[Any], configState: Option[String]) diff --git a/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatus.scala b/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatus.scala index f73e5675..3447f86b 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatus.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatus.scala @@ -1,5 +1,6 @@ package org.openhorizon.exchangeapi.table.node.status +import io.swagger.v3.oas.annotations.media.{ArraySchema, Schema} import org.openhorizon.exchangeapi.table.node.OneService final case class NodeStatus(connectivity: Map[String,Boolean], diff --git a/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatusRow.scala b/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatusRow.scala index 7d39e6dc..24d6ae65 100644 --- a/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatusRow.scala +++ b/src/main/scala/org/openhorizon/exchangeapi/table/node/status/NodeStatusRow.scala @@ -1,5 +1,6 @@ package org.openhorizon.exchangeapi.table.node.status +import io.swagger.v3.oas.annotations.media.{ArraySchema, Schema} import org.json4s.jackson.Serialization.read import org.json4s.{DefaultFormats, Formats} import org.openhorizon.exchangeapi.table.node.OneService diff --git a/src/test/java/org/openhorizon/exchangeapi/annotation/AdminStatusTest.java b/src/test/java/org/openhorizon/exchangeapi/annotation/AdminStatusTest.java new file mode 100644 index 00000000..1ebda826 --- /dev/null +++ b/src/test/java/org/openhorizon/exchangeapi/annotation/AdminStatusTest.java @@ -0,0 +1,12 @@ +package org.openhorizon.exchangeapi.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.scalatest.TagAnnotation; + +@TagAnnotation +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface AdminStatusTest {} diff --git a/src/test/scala/org/openhorizon/exchangeapi/AdminSuite.scala b/src/test/scala/org/openhorizon/exchangeapi/AdminSuite.scala index a43cc3cb..109da1f8 100644 --- a/src/test/scala/org/openhorizon/exchangeapi/AdminSuite.scala +++ b/src/test/scala/org/openhorizon/exchangeapi/AdminSuite.scala @@ -1,10 +1,9 @@ package org.openhorizon.exchangeapi -import org.openhorizon.exchangeapi.route.administration.{DeleteOrgChangesRequest, GetAdminOrgStatusResponse} +import org.openhorizon.exchangeapi.route.administration.{AdminHashpwResponse, AdminOrgStatus, AdminStatus, DeleteOrgChangesRequest} import scala.util.matching.Regex import org.json4s.DefaultFormats -import org.openhorizon.exchangeapi.route.administration.{AdminHashpwResponse, GetAdminOrgStatusResponse, GetAdminStatusResponse} import org.openhorizon.exchangeapi.table.organization.OrgRow import org.openhorizon.exchangeapi.utility.{ApiTime, Configuration} @@ -20,7 +19,7 @@ import org.scalatest.BeforeAndAfterAll import org.scalatest.funsuite.AnyFunSuite import org.scalatestplus.junit.JUnitRunner import org.openhorizon.exchangeapi.auth.{Password, Role} -import org.openhorizon.exchangeapi.route.administration.{AdminHashpwRequest, AdminHashpwResponse, GetAdminStatusResponse} +import org.openhorizon.exchangeapi.route.administration.{AdminHashpwRequest, AdminHashpwResponse} import org.openhorizon.exchangeapi.route.agreementbot.PutAgbotsRequest import org.openhorizon.exchangeapi.route.deploymentpattern.PostPutPatternRequest import org.openhorizon.exchangeapi.route.node.PutNodesRequest @@ -268,7 +267,7 @@ class AdminSuite extends AnyFunSuite with BeforeAndAfterAll { info("http status code: " + response.code) info("body: " + response.body) assert(response.code === HttpCode.OK.intValue) - val getResp = parse(response.body).extract[GetAdminStatusResponse] + val getResp = parse(response.body).extract[AdminStatus] assert(getResp.msg.contains("operating normally")) } } @@ -280,7 +279,7 @@ class AdminSuite extends AnyFunSuite with BeforeAndAfterAll { // info("headers: " + response.headers) info("body: " + response.body) assert(response.code === HttpCode.OK.intValue) - val getResp = parse(response.body).extract[GetAdminStatusResponse] + val getResp = parse(response.body).extract[AdminStatus] assert(getResp.msg.contains("operating normally")) } @@ -299,7 +298,7 @@ class AdminSuite extends AnyFunSuite with BeforeAndAfterAll { // info("headers: " + response.headers) info("body: " + response.body) assert(response.code === HttpCode.OK.intValue) - val getResp = parse(response.body).extract[GetAdminOrgStatusResponse] + val getResp = parse(response.body).extract[AdminOrgStatus] assert(getResp.msg.contains("operating normally")) assert(!getResp.nodes.isEmpty) // nodes should be visible assert(getResp.nodes.size >= ORGS.size) // account for orgs that might already exist in db, but there should be at least 2 @@ -321,12 +320,12 @@ class AdminSuite extends AnyFunSuite with BeforeAndAfterAll { info("http status code: " + response.code) info("body: " + response.body) assert(response.code === HttpCode.OK.intValue) - val getResp = parse(response.body).extract[GetAdminOrgStatusResponse] + val getResp = parse(response.body).extract[AdminOrgStatus] assert(getResp.msg.contains("operating normally")) assert(!getResp.nodes.isEmpty) // nodes should be visible assert(getResp.nodes.size >= ORGS.size) // account for orgs that might already exist in db, but there should be at least 2 assert(getResp.nodes.contains(ORGS.head)) - assert(getResp.nodes.contains(ORGS(1))) + assert(!getResp.nodes.contains(ORGS(1))) } // make a hubadmin @@ -342,7 +341,7 @@ class AdminSuite extends AnyFunSuite with BeforeAndAfterAll { info("http status code: " + response.code) info("body: " + response.body) assert(response.code === HttpCode.OK.intValue) - val getResp = parse(response.body).extract[GetAdminOrgStatusResponse] + val getResp = parse(response.body).extract[AdminOrgStatus] assert(getResp.msg.contains("operating normally")) assert(!getResp.nodes.isEmpty) // nodes should be visible assert(getResp.nodes.size >= ORGS.size) // account for orgs that might already exist in db, but there should be at least 2 diff --git a/src/test/scala/org/openhorizon/exchangeapi/route/administration/TestGetAdminStatus.scala b/src/test/scala/org/openhorizon/exchangeapi/route/administration/TestGetAdminStatus.scala new file mode 100644 index 00000000..2d0d2e61 --- /dev/null +++ b/src/test/scala/org/openhorizon/exchangeapi/route/administration/TestGetAdminStatus.scala @@ -0,0 +1,717 @@ +package org.openhorizon.exchangeapi.route.administration + +import org.json4s.DefaultFormats +import org.json4s.jackson.JsonMethods +import org.openhorizon.exchangeapi.auth.{Password, Role} +import org.openhorizon.exchangeapi.table.agreementbot.agreement.{AgbotAgreementRow, AgbotAgreementsTQ} +import org.openhorizon.exchangeapi.table.agreementbot.message.{AgbotMsgRow, AgbotMsgsTQ} +import org.openhorizon.exchangeapi.table.agreementbot.{AgbotRow, AgbotsTQ} +import org.openhorizon.exchangeapi.table.node.agreement.{NodeAgreementRow, NodeAgreementsTQ} +import org.openhorizon.exchangeapi.table.node.message.{NodeMsgRow, NodeMsgsTQ} +import org.openhorizon.exchangeapi.table.node.{NodeRow, NodesTQ} +import org.openhorizon.exchangeapi.table.organization.{OrgRow, OrgsTQ} +import org.openhorizon.exchangeapi.table.resourcechange.ResourceChangesTQ +import org.openhorizon.exchangeapi.table.schema.SchemaTQ +import org.openhorizon.exchangeapi.table.user.{UserRow, UsersTQ} +import org.openhorizon.exchangeapi.tag.AdminStatusTest +import org.openhorizon.exchangeapi.utility.ApiTime.fixFormatting +import org.openhorizon.exchangeapi.utility.{ApiTime, ApiUtils, Configuration, DatabaseConnection, ExchMsg, HttpCode} +import org.scalatest.BeforeAndAfterAll +import org.scalatest.funsuite.AnyFunSuite +import scalaj.http.{Http, HttpResponse} +import slick.jdbc +import slick.jdbc.PostgresProfile.api._ + +import java.sql.Timestamp +import java.time.ZoneId +import scala.concurrent.Await +import scala.concurrent.duration.{Duration, DurationInt} + + + +class TestGetAdminStatus extends AnyFunSuite with BeforeAndAfterAll { + + private val ACCEPT = ("Accept","application/json") + private val AWAITDURATION: Duration = 15.seconds + private val DBCONNECTION: jdbc.PostgresProfile.api.Database = DatabaseConnection.getDatabase + private val URL = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" + private val ROUTE = "/status" + private val SCHEMAVERSION: Int = Await.result(DBCONNECTION.run(SchemaTQ.getSchemaVersion.result), AWAITDURATION).head + + private implicit val formats: DefaultFormats.type = DefaultFormats + + private val PASSWORD = "password" + + private val TIMESTAMP: Timestamp = ApiTime.nowUTCTimestamp + private val TIMESTAMPSTRING: String = fixFormatting(TIMESTAMP.toInstant.atZone(ZoneId.of("UTC")).withZoneSameInstant(ZoneId.of("UTC")).toString) + + private val TESTORGS: Seq[OrgRow] = + Seq(OrgRow(description = "", + heartbeatIntervals = "", + label = "", + lastUpdated = TIMESTAMPSTRING, + limits = "", + orgId = "testGetOrgStatusRoute0", + orgType = "", + tags = None), + OrgRow(description = "", + heartbeatIntervals = "", + label = "", + lastUpdated = TIMESTAMPSTRING, + limits = "", + orgId = "testGetOrgStatusRoute1", + orgType = "", + tags = None)) + + private val TESTUSERS: Seq[UserRow] = + Seq(UserRow(admin = false, + email = "", + hashedPw = Password.hash(PASSWORD), + hubAdmin = true, + lastUpdated = TIMESTAMPSTRING, + orgid = "root", + updatedBy = "", + username = "root/TestGetOrgStatusRouteHubAdmin0"), + UserRow(admin = true, + email = "", + hashedPw = Password.hash(PASSWORD), + hubAdmin = false, + lastUpdated = TIMESTAMPSTRING, + orgid = "testGetOrgStatusRoute0", + updatedBy = "", + username = "testGetOrgStatusRoute0/admin0"), + UserRow(admin = false, + email = "", + hashedPw = Password.hash(PASSWORD), + hubAdmin = false, + lastUpdated = TIMESTAMPSTRING, + orgid = "testGetOrgStatusRoute0", + updatedBy = "", + username = "testGetOrgStatusRoute0/user0"), + UserRow(admin = false, + email = "", + hashedPw = Password.hash(PASSWORD), + hubAdmin = false, + lastUpdated = TIMESTAMPSTRING, + orgid = "testGetOrgStatusRoute1", + updatedBy = "", + username = "testGetOrgStatusRoute1/user0")) + + private val TESTNODES: Seq[NodeRow] = + Seq(NodeRow(arch = "", + id = TESTORGS(0).orgId + "/node0", + heartbeatIntervals = "", + lastHeartbeat = None, + lastUpdated = TIMESTAMPSTRING, + msgEndPoint = "", + name = "", + nodeType = "", + orgid = TESTORGS(0).orgId, + owner = TESTUSERS(1).username, + pattern = "", + publicKey = "", + regServices = "", + softwareVersions = "", + token = Password.hash(PASSWORD), + userInput = ""), + NodeRow(arch = "", + id = TESTORGS(0).orgId + "/node1", + heartbeatIntervals = "", + lastHeartbeat = None, + lastUpdated = TIMESTAMPSTRING, + msgEndPoint = "", + name = "", + nodeType = "", + orgid = TESTORGS(0).orgId, + owner = TESTUSERS(2).username, + pattern = "", + publicKey = "registered", + regServices = "", + softwareVersions = "", + token = "", + userInput = ""), + NodeRow(arch = "", + id = TESTORGS(1).orgId + "/node0", + heartbeatIntervals = "", + lastHeartbeat = None, + lastUpdated = TIMESTAMPSTRING, + msgEndPoint = "", + name = "", + nodeType = "", + orgid = TESTORGS(1).orgId, + owner = TESTUSERS(3).username, + pattern = "", + publicKey = "", + regServices = "", + softwareVersions = "", + token = "", + userInput = "")) + + private val TESTAGBOTS: Seq[AgbotRow] = //must have an agbot in order to create a node message + Seq(AgbotRow(id = "IBM/testGetOrgStatusRoute0", + lastHeartbeat = TIMESTAMPSTRING, + msgEndPoint = "", + name = "testAgbot", + orgid = "IBM", + owner = TESTUSERS(0).username, + publicKey = "", + token = Password.hash(PASSWORD)), + AgbotRow(id = TESTORGS(0).orgId + "/agbot0", + lastHeartbeat = TIMESTAMPSTRING, + msgEndPoint = "", + name = "testAgbot", + orgid = TESTORGS(0).orgId, + owner = TESTUSERS(1).username, + publicKey = "", + token = Password.hash(PASSWORD)), + AgbotRow(id = TESTORGS(0).orgId + "/agbot1", + lastHeartbeat = TIMESTAMPSTRING, + msgEndPoint = "", + name = "testAgbot", + orgid = TESTORGS(0).orgId, + owner = TESTUSERS(2).username, + publicKey = "", + token = ""), + AgbotRow(id = TESTORGS(1).orgId + "/agbot0", + lastHeartbeat = TIMESTAMPSTRING, + msgEndPoint = "", + name = "testAgbot", + orgid = TESTORGS(1).orgId, + owner = TESTUSERS(3).username, + publicKey = "", + token = ""), + ) + + private val TESTAGBOTAGREEMENTS: Seq[AgbotAgreementRow] = + Seq(AgbotAgreementRow(agrId = "TestGetOrgStatusRoute0", + agbotId = TESTAGBOTS(0).id, + dataLastReceived = "", + lastUpdated = TIMESTAMPSTRING, + serviceOrgid = "IBM", + servicePattern = "", + serviceUrl = "", + state = "active"), + AgbotAgreementRow(agrId = "TestGetOrgStatusRoute1", + agbotId = TESTAGBOTS(1).id, + dataLastReceived = "", + lastUpdated = TIMESTAMPSTRING, + serviceOrgid = TESTORGS(0).orgId, + servicePattern = "", + serviceUrl = "", + state = "active"), + AgbotAgreementRow(agrId = "TestGetOrgStatusRoute2", + agbotId = TESTAGBOTS(2).id, + dataLastReceived = "", + lastUpdated = TIMESTAMPSTRING, + serviceOrgid = TESTORGS(0).orgId, + servicePattern = "", + serviceUrl = "", + state = "active"), + AgbotAgreementRow(agrId = "TestGetOrgStatusRoute3", + agbotId = TESTAGBOTS(3).id, + dataLastReceived = "", + lastUpdated = TIMESTAMPSTRING, + serviceOrgid = TESTORGS(1).orgId, + servicePattern = "", + serviceUrl = "", + state = "active")) + + private val TESTNODEAGREEMENTS: Seq[NodeAgreementRow] = + Seq(NodeAgreementRow(agId = "TestGetOrgStatusRoute0", + agrSvcOrgid = TESTORGS(0).orgId, + agrSvcPattern = "", + agrSvcUrl = "", + lastUpdated = TIMESTAMPSTRING, + nodeId = TESTNODES(0).id, + services = "", + state = "active"), + NodeAgreementRow(agId = "TestGetOrgStatusRoute1", + agrSvcOrgid = TESTORGS(0).orgId, + agrSvcPattern = "", + agrSvcUrl = "", + lastUpdated = TIMESTAMPSTRING, + nodeId = TESTNODES(1).id, + services = "", + state = "active"), + NodeAgreementRow(agId = "TestGetOrgStatusRoute2", + agrSvcOrgid = TESTORGS(1).orgId, + agrSvcPattern = "", + agrSvcUrl = "", + lastUpdated = TIMESTAMPSTRING, + nodeId = TESTNODES(2).id, + services = "", + state = "active")) + + private val TESTAGBOTMESSAGES: Seq[AgbotMsgRow] = + Seq(AgbotMsgRow(agbotId = TESTAGBOTS(0).id, + message = "", + msgId = 0, + nodeId = TESTNODES(0).id, + nodePubKey = "", + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING), + AgbotMsgRow(agbotId = TESTAGBOTS(1).id, + message = "", + msgId = 0, + nodeId = TESTNODES(0).id, + nodePubKey = "", + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING), + AgbotMsgRow(agbotId = TESTAGBOTS(2).id, + message = "", + msgId = 0, + nodeId = TESTNODES(1).id, + nodePubKey = "", + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING), + AgbotMsgRow(agbotId = TESTAGBOTS(3).id, + message = "", + msgId = 0, + nodeId = TESTNODES(2).id, + nodePubKey = "", + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING)) + + private val TESTNODEMESSAGES: Seq[NodeMsgRow] = + Seq(NodeMsgRow(agbotId = TESTAGBOTS(1).id, + agbotPubKey = "", + message = "", + msgId = 0, // this will be automatically set to a unique ID by the DB + nodeId = TESTNODES(0).id, + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING), + NodeMsgRow(agbotId = TESTAGBOTS(2).id, + agbotPubKey = "", + message = "", + msgId = 0, // this will be automatically set to a unique ID by the DB + nodeId = TESTNODES(1).id, + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING), + NodeMsgRow(agbotId = TESTAGBOTS(3).id, + agbotPubKey = "", + message = "", + msgId = 0, // this will be automatically set to a unique ID by the DB + nodeId = TESTNODES(2).id, + timeExpires = ApiTime.futureUTC(120), + timeSent = TIMESTAMPSTRING)) + + private val ROOTAUTH = ("Authorization","Basic " + ApiUtils.encode(Role.superUser + ":" + (try Configuration.getConfig.getString("api.root.password") catch { case _: Exception => "" }))) + private val HUBADMINAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTUSERS(0).username + ":" + PASSWORD)) + private val USERAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTUSERS(2).username + ":" + PASSWORD)) + private val ORGADMINAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTUSERS(1).username + ":" + PASSWORD)) + private val NODEAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTNODES(0).id + ":" + PASSWORD)) + private val MULTITENANTAGBOTAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTAGBOTS(0).id + ":" + PASSWORD)) + private val AGBOTAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTAGBOTS(1).id + ":" + PASSWORD)) + + override def beforeAll(): Unit = { + Await.ready(DBCONNECTION.run((OrgsTQ ++= TESTORGS) andThen + (UsersTQ ++= TESTUSERS) andThen + (AgbotsTQ ++= TESTAGBOTS) andThen + (NodesTQ ++= TESTNODES) andThen + (NodeMsgsTQ ++= TESTNODEMESSAGES) andThen + (NodeAgreementsTQ ++= TESTNODEAGREEMENTS) andThen + (AgbotMsgsTQ ++= TESTAGBOTMESSAGES) andThen + (AgbotAgreementsTQ ++= TESTAGBOTAGREEMENTS)), AWAITDURATION) + } + + override def afterAll(): Unit = { + Await.ready(DBCONNECTION.run(ResourceChangesTQ.filter(_.orgId startsWith "testGetOrgStatusRoute").delete andThen + OrgsTQ.filter(_.orgid startsWith "testGetOrgStatusRoute").delete andThen + UsersTQ.filter(_.username startsWith "root/TestGetOrgStatusRouteHubAdmin").delete), AWAITDURATION) + } + + + //is this intended? I would think this should fail with 404 not found + test("GET /orgs/doesNotExist" + ROUTE + " -- root - 404 not found - Organization does not exist") { + val response: HttpResponse[String] = Http(URL + "doesNotExist" + ROUTE).headers(ACCEPT).headers(ROOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.NOT_FOUND.intValue) + } + + test("GET /orgs/" + TESTORGS(1).orgId + ROUTE + " -- user - 403 access denied - as user in other org") { + val response: HttpResponse[String] = Http(URL + TESTORGS(1).orgId + ROUTE).headers(ACCEPT).headers(USERAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.ACCESS_DENIED.intValue) + } + + test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " -- root - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(ROOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 2) + assert(status.numberOfAgbotMsgs === 2) + assert(status.numberOfAgbots === 2) + assert(status.numberOfNodeAgreements.get === 2) + assert(status.numberOfNodeMsgs.get === 2) + assert(status.numberOfNodes.get === 2) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 1) + assert(status.numberOfUsers.get === 2) + assert(status.SchemaVersion.get === SCHEMAVERSION) + } + + test("GET /orgs/" + TESTORGS(1).orgId + ROUTE + " -- root - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(1).orgId + ROUTE).headers(ACCEPT).headers(ROOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 1) + assert(status.numberOfAgbotMsgs === 1) + assert(status.numberOfAgbots === 1) + assert(status.numberOfNodeAgreements.get === 1) + assert(status.numberOfNodeMsgs.get === 1) + assert(status.numberOfNodes.get === 1) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 0) + assert(status.numberOfUnregisteredNodes.get === 1) + assert(status.numberOfUsers.get === 1) + assert(status.SchemaVersion.get === SCHEMAVERSION) + } + + test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " -- hub admin - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(HUBADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 2) + assert(status.numberOfAgbotMsgs === 2) + assert(status.numberOfAgbots === 2) + assert(status.numberOfNodeAgreements.get === -1) + assert(status.numberOfNodeMsgs.get === -1) + assert(status.numberOfNodes.get === -1) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.isEmpty) + assert(status.numberOfUnregisteredNodes.isEmpty) + assert(status.numberOfUsers.get === 2) + assert(status.SchemaVersion.get === SCHEMAVERSION) + } + + test("GET /orgs/" + TESTORGS(1).orgId + ROUTE + " -- hub admin - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(1).orgId + ROUTE).headers(ACCEPT).headers(HUBADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 1) + assert(status.numberOfAgbotMsgs === 1) + assert(status.numberOfAgbots === 1) + assert(status.numberOfNodes.get === -1) + assert(status.numberOfNodeMsgs.get === -1) + assert(status.numberOfNodeAgreements.get === -1) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.isEmpty) + assert(status.numberOfUnregisteredNodes.isEmpty) + assert(status.numberOfUsers.get === 1) + assert(status.SchemaVersion.get === SCHEMAVERSION) + } + + test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " -- organization admin - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(ORGADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 2) + assert(status.numberOfAgbotMsgs === 2) + assert(status.numberOfAgbots === 2) + assert(status.numberOfNodes.get === 2) + assert(status.numberOfNodeMsgs.get === 2) + assert(status.numberOfNodeAgreements.get === 2) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 1) + assert(status.numberOfUsers.get === 2) + assert(status.SchemaVersion.get === -1) + } + + test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " -- user - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(USERAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 2) + assert(status.numberOfAgbotMsgs === 2) + assert(status.numberOfAgbots === 2) + assert(status.numberOfNodeAgreements.get === 1) + assert(status.numberOfNodeMsgs.get === 1) + assert(status.numberOfNodes.get === 1) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 0) + assert(status.numberOfUsers.get === 1) + assert(status.SchemaVersion.get === -1) + } + + test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " -- agbot - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(AGBOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 2) + assert(status.numberOfAgbotMsgs === 2) + assert(status.numberOfAgbots === 2) + assert(status.numberOfNodes.get === 2) + assert(status.numberOfNodeMsgs.get === 2) + assert(status.numberOfNodeAgreements.get === 2) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 1) + assert(status.numberOfUsers.get === -1) + assert(status.SchemaVersion.get === -1) + } + + test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " -- node - 200 ok - normal success") { + val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(NODEAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 2) + assert(status.numberOfAgbotMsgs === 2) + assert(status.numberOfAgbots === 2) + assert(status.numberOfNodes.get === 1) + assert(status.numberOfNodeMsgs.get === 1) + assert(status.numberOfNodeAgreements.get === 1) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 0) + assert(status.numberOfUnregisteredNodes.get === 1) + assert(status.numberOfUsers.get === -1) + assert(status.SchemaVersion.get === -1) + } + + test("GET /orgs/" + "IBM" + ROUTE + " -- node - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http(URL + "IBM" + ROUTE).headers(ACCEPT).headers(NODEAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.isEmpty) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 1) + assert(status.numberOfAgbotMsgs === 1) + assert(status.numberOfAgbots === 1) + assert(status.numberOfNodes.get === 0) + assert(status.numberOfNodeMsgs.get === 0) + assert(status.numberOfNodeAgreements.get === 0) + assert(status.numberOfOrganizations === 1) + assert(status.numberOfRegisteredNodes.get === 0) + assert(status.numberOfUnregisteredNodes.get === 0) + assert(status.numberOfUsers.get === -1) + assert(status.SchemaVersion.get === -1) + } + + // --------------- .../v1/admin/status --------------- + // [WARNING] No test suite isolation! + // Run these test cases by themselves. $ sbt onlyAdminStatusTests + + test("GET /admin/status" + " -- agbot - 403 access denied", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/status").headers(ACCEPT).headers(AGBOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.ACCESS_DENIED.intValue) + } + + test("GET /admin/status" + " -- node - 403 access denied", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/status").headers(ACCEPT).headers(NODEAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.ACCESS_DENIED.intValue) + } + + test("GET /admin/status" + " -- root - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/status").headers(ACCEPT).headers(ROOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.get === SCHEMAVERSION) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 4) + assert(status.numberOfAgbotMsgs === 4) + assert(status.numberOfAgbots === 4) + assert(status.numberOfNodeAgreements.get === 3) + assert(status.numberOfNodeMsgs.get === 3) + assert(status.numberOfNodes.get === 3) + assert(status.numberOfOrganizations === 4) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 2) + assert(status.numberOfUsers.get === 5) + assert(status.SchemaVersion.isEmpty) + } + + test("GET /admin/status" + " -- hub admin - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/status").headers(ACCEPT).headers(HUBADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.get === SCHEMAVERSION) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 4) + assert(status.numberOfAgbotMsgs === 4) + assert(status.numberOfAgbots === 4) + assert(status.numberOfNodeAgreements.get === -1) + assert(status.numberOfNodeMsgs.get === -1) + assert(status.numberOfNodes.get === -1) + assert(status.numberOfOrganizations === 4) + assert(status.numberOfRegisteredNodes.isEmpty) + assert(status.numberOfUnregisteredNodes.isEmpty) + assert(status.numberOfUsers.get === 5) + assert(status.SchemaVersion.isEmpty) + } + + test("GET /admin/status" + " -- organization admin - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/status").headers(ACCEPT).headers(ORGADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.get === -1) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 3) + assert(status.numberOfAgbotMsgs === 3) + assert(status.numberOfAgbots === 3) + assert(status.numberOfNodeAgreements.get === 2) + assert(status.numberOfNodeMsgs.get === 2) + assert(status.numberOfNodes.get === 2) + assert(status.numberOfOrganizations === 2) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 1) + assert(status.numberOfUsers.get === 2) + assert(status.SchemaVersion.isEmpty) + } + + test("GET /admin/status" + " -- user - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/status").headers(ACCEPT).headers(USERAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminStatus = JsonMethods.parse(response.body).extract[AdminStatus] + assert(status.dbSchemaVersion.get === -1) + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.numberOfAgbotAgreements === 3) + assert(status.numberOfAgbotMsgs === 3) + assert(status.numberOfAgbots === 3) + assert(status.numberOfNodeAgreements.get === 1) + assert(status.numberOfNodeMsgs.get === 1) + assert(status.numberOfNodes.get === 1) + assert(status.numberOfOrganizations === 2) + assert(status.numberOfRegisteredNodes.get === 1) + assert(status.numberOfUnregisteredNodes.get === 0) + assert(status.numberOfUsers.get === 1) + assert(status.SchemaVersion.isEmpty) + } + + + // --------------- .../v1/admin/orgstatus --------------- + // [WARNING] No test suite isolation! + // Run these test cases by themselves. $ sbt onlyAdminStatusTests + + test("GET /admin/orgstatus" + " -- agbot - 403 access denied", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/orgstatus").headers(ACCEPT).headers(AGBOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.ACCESS_DENIED.intValue) + } + + test("GET /admin/orgstatus" + " -- node - 403 access denied", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/orgstatus").headers(ACCEPT).headers(NODEAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.ACCESS_DENIED.intValue) + } + + test("GET /admin/orgstatus" + " -- user - 403 access denied", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/orgstatus").headers(ACCEPT).headers(USERAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.ACCESS_DENIED.intValue) + /*assert(response.code === HttpCode.OK.intValue) + val status: AdminOrgStatus = JsonMethods.parse(response.body).extract[AdminOrgStatus] + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.nodes.size === 2) + assert(status.nodes.contains("IBM")) + assert(status.nodes("IBM") === 0) + assert(!status.nodes.contains("root")) + assert(status.nodes.contains(TESTORGS.head.orgId)) + assert(status.nodes(TESTORGS.head.orgId) === 1) + assert(!status.nodes.contains(TESTORGS(1).orgId))*/ + } + + test("GET /admin/orgstatus" + " -- root - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/orgstatus").headers(ACCEPT).headers(ROOTAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminOrgStatus = JsonMethods.parse(response.body).extract[AdminOrgStatus] + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.nodes.size === 4) + assert(status.nodes.contains("IBM")) + assert(status.nodes("IBM") === 0) + assert(status.nodes.contains("root")) + assert(status.nodes("root") === 0) + assert(status.nodes.contains(TESTORGS.head.orgId)) + assert(status.nodes(TESTORGS.head.orgId) === 2) + assert(status.nodes.contains(TESTORGS(1).orgId)) + assert(status.nodes(TESTORGS(1).orgId) === 1) + } + + test("GET /admin/orgstatus" + " -- hub admin - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/orgstatus").headers(ACCEPT).headers(HUBADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminOrgStatus = JsonMethods.parse(response.body).extract[AdminOrgStatus] + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.nodes.size === 4) + assert(status.nodes.contains("IBM")) + assert(status.nodes("IBM") === -1) + assert(status.nodes.contains("root")) + assert(status.nodes("root") === -1) + assert(status.nodes.contains(TESTORGS.head.orgId)) + assert(status.nodes(TESTORGS.head.orgId) === -1) + assert(status.nodes.contains(TESTORGS(1).orgId)) + assert(status.nodes(TESTORGS(1).orgId) === -1) + } + + test("GET /admin/orgstatus" + " -- organization admin - 200 ok - normal success", AdminStatusTest) { + val response: HttpResponse[String] = Http("http://0.0.0.0:8080/v1/" + "admin/orgstatus").headers(ACCEPT).headers(ORGADMINAUTH).asString + info("Code: " + response.code) + info("Body: " + response.body) + assert(response.code === HttpCode.OK.intValue) + val status: AdminOrgStatus = JsonMethods.parse(response.body).extract[AdminOrgStatus] + assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) + assert(status.nodes.size === 2) + assert(status.nodes.contains("IBM")) + assert(status.nodes("IBM") === 0) + assert(!status.nodes.contains("root")) + assert(status.nodes.contains(TESTORGS.head.orgId)) + assert(status.nodes(TESTORGS.head.orgId) === 2) + assert(!status.nodes.contains(TESTORGS(1).orgId)) + } +} diff --git a/src/test/scala/org/openhorizon/exchangeapi/route/organization/TestGetOrgStatusRoute.scala b/src/test/scala/org/openhorizon/exchangeapi/route/organization/TestGetOrgStatusRoute.scala deleted file mode 100644 index dbb54f8a..00000000 --- a/src/test/scala/org/openhorizon/exchangeapi/route/organization/TestGetOrgStatusRoute.scala +++ /dev/null @@ -1,284 +0,0 @@ -package org.openhorizon.exchangeapi.route.organization - -import org.json4s.DefaultFormats -import org.json4s.jackson.JsonMethods -import org.openhorizon.exchangeapi.auth.{Password, Role} -import org.openhorizon.exchangeapi.table.agreementbot.{AgbotRow, AgbotsTQ} -import org.openhorizon.exchangeapi.table.node.agreement.{NodeAgreementRow, NodeAgreementsTQ} -import org.openhorizon.exchangeapi.table.node.{NodeRow, NodesTQ} -import org.openhorizon.exchangeapi.table.node.message.{NodeMsgRow, NodeMsgsTQ} -import org.openhorizon.exchangeapi.table.organization.{OrgRow, OrgsTQ} -import org.openhorizon.exchangeapi.table.resourcechange.ResourceChangesTQ -import org.openhorizon.exchangeapi.table.schema.SchemaTQ -import org.openhorizon.exchangeapi.table.user.{UserRow, UsersTQ} -import org.openhorizon.exchangeapi.utility.{ApiTime, ApiUtils, Configuration, DatabaseConnection, ExchMsg, HttpCode} -import org.scalatest.BeforeAndAfterAll -import org.scalatest.funsuite.AnyFunSuite -import scalaj.http.{Http, HttpResponse} -import slick.jdbc - -import scala.concurrent.Await -import scala.concurrent.duration.{Duration, DurationInt} -import slick.jdbc.PostgresProfile.api._ - -class TestGetOrgStatusRoute extends AnyFunSuite with BeforeAndAfterAll { - - private val ACCEPT = ("Accept","application/json") - private val AWAITDURATION: Duration = 15.seconds - private val DBCONNECTION: jdbc.PostgresProfile.api.Database = DatabaseConnection.getDatabase - private val URL = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") + "/v1/orgs/" - private val ROUTE = "/status" - private val SCHEMAVERSION: Int = Await.result(DBCONNECTION.run(SchemaTQ.getSchemaVersion.result), AWAITDURATION).head - - private implicit val formats: DefaultFormats.type = DefaultFormats - - private val HUBADMINPASSWORD = "adminpassword" - private val USER1PASSWORD = "user1password" - private val USER2PASSWORD = "user2password" - - private val TESTORGS: Seq[OrgRow] = - Seq( - OrgRow( - heartbeatIntervals = - """ - |{ - | "minInterval": 1, - | "maxInterval": 2, - | "intervalAdjustment": 3 - |} - |""".stripMargin, - description = "Test Organization 1", - label = "testGetOrgStatus", - lastUpdated = ApiTime.nowUTC, - limits = - """ - |{ - | "maxNodes": 100 - |} - |""".stripMargin, - orgId = "testGetOrgStatusRoute1", - orgType = "testGetOrgStatus", - tags = None), - OrgRow( - heartbeatIntervals = - """ - |{ - | "minInterval": 4, - | "maxInterval": 5, - | "intervalAdjustment": 6 - |} - |""".stripMargin, - description = "Test Organization 2", - label = "sampleGetOrgStatus", - lastUpdated = ApiTime.nowUTC, - limits = - """ - |{ - | "maxNodes": 5 - |} - |""".stripMargin, - orgId = "testGetOrgStatusRoute2", - orgType = "testGetOrgStatus", - tags = Some(JsonMethods.parse( - """ - |{ - | "tagName": "tagValue" - |} - |""".stripMargin - )))) - - private val TESTUSERS: Seq[UserRow] = - Seq( - UserRow( - username = "root/TestGetOrgStatusRouteHubAdmin", - orgid = "root", - hashedPw = Password.hash(HUBADMINPASSWORD), - admin = false, - hubAdmin = true, - email = "TestGetOrgStatusRouteHubAdmin@ibm.com", - lastUpdated = ApiTime.nowUTC, - updatedBy = "root" - ), - UserRow( - username = TESTORGS(0).orgId + "/TestGetOrgStatusRouteUser1", - orgid = TESTORGS(0).orgId, - hashedPw = Password.hash(USER1PASSWORD), - admin = false, - hubAdmin = false, - email = "TestGetOrgStatusRouteUser1@ibm.com", - lastUpdated = ApiTime.nowUTC, - updatedBy = "root/root" - ), - UserRow( - username = TESTORGS(1).orgId + "/TestGetOrgStatusRouteUser2", - orgid = TESTORGS(1).orgId, - hashedPw = Password.hash(USER2PASSWORD), - admin = false, - hubAdmin = false, - email = "TestGetOrgStatusRouteUser2@ibm.com", - lastUpdated = ApiTime.nowUTC, - updatedBy = "root/root" - ) - ) - - private val TESTNODES: Seq[NodeRow] = - Seq( - NodeRow( - arch = "", - id = TESTORGS(0).orgId + "/n1", - heartbeatIntervals = "", - lastHeartbeat = None, - lastUpdated = ApiTime.nowUTC, - msgEndPoint = "", - name = "", - nodeType = "", - orgid = TESTORGS(0).orgId, - owner = TESTUSERS(1).username, - pattern = "", - publicKey = "", - regServices = "", - softwareVersions = "", - token = "", - userInput = ""), - NodeRow( - arch = "", - id = TESTORGS(0).orgId + "/n2", - heartbeatIntervals = "", - lastHeartbeat = None, - lastUpdated = ApiTime.nowUTC, - msgEndPoint = "", - name = "", - nodeType = "", - orgid = TESTORGS(0).orgId, - owner = TESTUSERS(1).username, - pattern = "", - publicKey = "registered", - regServices = "", - softwareVersions = "", - token = "", - userInput = "")) - - private val TESTAGBOTS: Seq[AgbotRow] = //must have an agbot in order to create a node message - Seq(AgbotRow( - id = TESTORGS(0).orgId + "/a1", - orgid = TESTORGS(0).orgId, - token = "", - name = "testAgbot", - owner = TESTUSERS(1).username, - msgEndPoint = "", - lastHeartbeat = ApiTime.nowUTC, - publicKey = "" - )) - - private val TESTAGREEMENTS: Seq[NodeAgreementRow] = - Seq(NodeAgreementRow( - agId = TESTAGBOTS(0).id, - nodeId = TESTNODES(0).id, - services = "", - agrSvcOrgid = TESTORGS(0).orgId, - agrSvcPattern = "", - agrSvcUrl = "", - state = "active", - lastUpdated = ApiTime.nowUTC)) - - private val TESTMESSAGES: Seq[NodeMsgRow] = - Seq(NodeMsgRow( - msgId = 0, // this will be automatically set to a unique ID by the DB - nodeId = TESTNODES(0).id, - agbotId = TESTAGBOTS(0).id, - agbotPubKey = "", - message = "Test Message", - timeSent = ApiTime.nowUTC, - timeExpires = ApiTime.futureUTC(120))) - - private val ROOTAUTH = ("Authorization","Basic " + ApiUtils.encode(Role.superUser + ":" + (try Configuration.getConfig.getString("api.root.password") catch { case _: Exception => "" }))) - private val HUBADMINAUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTUSERS(0).username + ":" + HUBADMINPASSWORD)) - private val USER1AUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTUSERS(1).username + ":" + USER1PASSWORD)) - private val USER2AUTH = ("Authorization", "Basic " + ApiUtils.encode(TESTUSERS(2).username + ":" + USER2PASSWORD)) - - override def beforeAll(): Unit = { - Await.ready(DBCONNECTION.run( - (OrgsTQ ++= TESTORGS) andThen - (UsersTQ ++= TESTUSERS) andThen - (AgbotsTQ ++= TESTAGBOTS) andThen - (NodesTQ ++= TESTNODES) andThen - (NodeMsgsTQ ++= TESTMESSAGES) andThen - (NodeAgreementsTQ ++= TESTAGREEMENTS)), AWAITDURATION - ) - } - - override def afterAll(): Unit = { - Await.ready(DBCONNECTION.run(ResourceChangesTQ.filter(_.orgId startsWith "testGetOrgStatusRoute").delete andThen - OrgsTQ.filter(_.orgid startsWith "testGetOrgStatusRoute").delete andThen - UsersTQ.filter(_.username startsWith "root/TestGetOrgStatusRouteHubAdmin").delete), AWAITDURATION) - } - - //is this intended? I would think this should fail with 404 not found - test("GET /orgs/doesNotExist" + ROUTE + " -- success, but returns 0 for everything") { - val response: HttpResponse[String] = Http(URL + "doesNotExist" + ROUTE).headers(ACCEPT).headers(ROOTAUTH).asString - info("Code: " + response.code) - info("Body: " + response.body) - assert(response.code === HttpCode.OK.intValue) - val status: GetOrgStatusResponse = JsonMethods.parse(response.body).extract[GetOrgStatusResponse] - assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) - assert(status.numberOfNodes === 0) - assert(status.numberOfUsers === 0) - assert(status.numberOfNodeMsgs === 0) - assert(status.numberOfNodeAgreements === 0) - assert(status.numberOfRegisteredNodes === 0) - assert(status.SchemaVersion === SCHEMAVERSION) - } - - test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " as root -- normal success") { - val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(ROOTAUTH).asString - info("Code: " + response.code) - info("Body: " + response.body) - assert(response.code === HttpCode.OK.intValue) - val status: GetOrgStatusResponse = JsonMethods.parse(response.body).extract[GetOrgStatusResponse] - assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) - assert(status.numberOfNodes === 2) - assert(status.numberOfUsers === 1) - assert(status.numberOfNodeMsgs === 1) - assert(status.numberOfNodeAgreements === 1) - assert(status.numberOfRegisteredNodes === 1) - assert(status.SchemaVersion === SCHEMAVERSION) - } - - test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " as hub admin -- normal success") { - val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(HUBADMINAUTH).asString - info("Code: " + response.code) - info("Body: " + response.body) - assert(response.code === HttpCode.OK.intValue) - val status: GetOrgStatusResponse = JsonMethods.parse(response.body).extract[GetOrgStatusResponse] - assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) - assert(status.numberOfNodes === 2) - assert(status.numberOfUsers === 1) - assert(status.numberOfNodeMsgs === 1) - assert(status.numberOfNodeAgreements === 1) - assert(status.numberOfRegisteredNodes === 1) - assert(status.SchemaVersion === SCHEMAVERSION) - } - - test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " as user in org -- normal success") { - val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(USER1AUTH).asString - info("Code: " + response.code) - info("Body: " + response.body) - assert(response.code === HttpCode.OK.intValue) - val status: GetOrgStatusResponse = JsonMethods.parse(response.body).extract[GetOrgStatusResponse] - assert(status.msg === ExchMsg.translate("exchange.server.operating.normally")) - assert(status.numberOfNodes === 2) - assert(status.numberOfUsers === 1) - assert(status.numberOfNodeMsgs === 1) - assert(status.numberOfNodeAgreements === 1) - assert(status.numberOfRegisteredNodes === 1) - assert(status.SchemaVersion === SCHEMAVERSION) - } - - test("GET /orgs/" + TESTORGS(0).orgId + ROUTE + " as user in other org -- 403 access denied") { - val response: HttpResponse[String] = Http(URL + TESTORGS(0).orgId + ROUTE).headers(ACCEPT).headers(USER2AUTH).asString - info("Code: " + response.code) - info("Body: " + response.body) - assert(response.code === HttpCode.ACCESS_DENIED.intValue) - } - -} diff --git a/src/test/scala/org/openhorizon/exchangeapi/tag/AdminStatusTest.scala b/src/test/scala/org/openhorizon/exchangeapi/tag/AdminStatusTest.scala new file mode 100644 index 00000000..0625109d --- /dev/null +++ b/src/test/scala/org/openhorizon/exchangeapi/tag/AdminStatusTest.scala @@ -0,0 +1,5 @@ +package org.openhorizon.exchangeapi.tag + +import org.scalatest.Tag + +object AdminStatusTest extends Tag("org.openhorizon.exchangeapi.tag.AdminStatusTest") diff --git a/src/test/scala/org/openhorizon/exchangeapi/utility/version/TestVersion.scala b/src/test/scala/org/openhorizon/exchangeapi/utility/TestVersion.scala similarity index 92% rename from src/test/scala/org/openhorizon/exchangeapi/utility/version/TestVersion.scala rename to src/test/scala/org/openhorizon/exchangeapi/utility/TestVersion.scala index aacf7cb8..db0c875f 100644 --- a/src/test/scala/org/openhorizon/exchangeapi/utility/version/TestVersion.scala +++ b/src/test/scala/org/openhorizon/exchangeapi/utility/TestVersion.scala @@ -1,12 +1,9 @@ -//package org.openhorizon.exchangeapi.route.version +package org.openhorizon.exchangeapi.utility -package org.openhorizon.exchangeapi +import org.junit.runner.RunWith import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import org.junit.runner.RunWith import org.scalatestplus.junit.JUnitRunner -import org.openhorizon.exchangeapi._ -import org.openhorizon.exchangeapi.utility.{Version, VersionRange} /** * Tests for the Version diff --git a/src/test/scala/org/openhorizon/exchangeapi/utility/version/TestVersionRange.scala b/src/test/scala/org/openhorizon/exchangeapi/utility/TestVersionRange.scala similarity index 95% rename from src/test/scala/org/openhorizon/exchangeapi/utility/version/TestVersionRange.scala rename to src/test/scala/org/openhorizon/exchangeapi/utility/TestVersionRange.scala index 6e370448..00e50d0c 100644 --- a/src/test/scala/org/openhorizon/exchangeapi/utility/version/TestVersionRange.scala +++ b/src/test/scala/org/openhorizon/exchangeapi/utility/TestVersionRange.scala @@ -1,10 +1,9 @@ -package org.openhorizon.exchangeapi +package org.openhorizon.exchangeapi.utility + +import org.junit.runner.RunWith import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import org.junit.runner.RunWith import org.scalatestplus.junit.JUnitRunner -import org.openhorizon.exchangeapi._ -import org.openhorizon.exchangeapi.utility.{Version, VersionRange} /** * Tests for the Version Range