From b8cd62661be2602ed150c1922ce6930e2bff9dd4 Mon Sep 17 00:00:00 2001 From: Simon Dumas Date: Mon, 25 Nov 2024 11:05:59 +0100 Subject: [PATCH] Delete Jira plugin --- build.sbt | 36 +-- .../plugins/jira/src/main/resources/jira.conf | 16 -- .../nexus/delta/plugins/jira/JiraClient.scala | 237 ------------------ .../nexus/delta/plugins/jira/JiraError.scala | 82 ------ .../nexus/delta/plugins/jira/JiraPlugin.scala | 9 - .../delta/plugins/jira/JiraPluginDef.scala | 17 -- .../delta/plugins/jira/JiraPluginModule.scala | 50 ---- .../nexus/delta/plugins/jira/OAuthToken.scala | 46 ---- .../nexus/delta/plugins/jira/TokenStore.scala | 64 ----- .../plugins/jira/config/JiraConfig.scala | 39 --- .../jira/model/AuthenticationRequest.scala | 21 -- .../plugins/jira/model/JiraResponse.scala | 28 --- .../delta/plugins/jira/model/Verifier.scala | 16 -- .../plugins/jira/routes/JiraRoutes.scala | 104 -------- .../delta/plugins/jira/TokenStoreSuite.scala | 41 --- .../scripts/postgres/drop/drop-tables.ddl | 1 - .../V1_11_M07_001__jira_drop_jira_tokens.ddl | 2 + .../delta/api/assets/jira/access-token.sh | 4 - .../assets/jira/request-token-response.json | 3 - .../delta/api/assets/jira/request-token.sh | 1 - docs/src/main/paradox/docs/delta/api/index.md | 1 - docs/src/main/paradox/docs/delta/api/jira.md | 93 ------- .../main/paradox/docs/delta/plugins/index.md | 1 - docs/src/main/paradox/docs/releases/index.md | 2 +- .../docs/running-nexus/configuration/index.md | 7 - 25 files changed, 5 insertions(+), 916 deletions(-) delete mode 100644 delta/plugins/jira/src/main/resources/jira.conf delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraClient.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraError.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPlugin.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginDef.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginModule.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/OAuthToken.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStore.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/config/JiraConfig.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/AuthenticationRequest.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/JiraResponse.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/Verifier.scala delete mode 100644 delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/routes/JiraRoutes.scala delete mode 100644 delta/plugins/jira/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStoreSuite.scala create mode 100644 delta/sourcing-psql/src/main/resources/scripts/postgres/init/V1_11_M07_001__jira_drop_jira_tokens.ddl delete mode 100644 docs/src/main/paradox/docs/delta/api/assets/jira/access-token.sh delete mode 100644 docs/src/main/paradox/docs/delta/api/assets/jira/request-token-response.json delete mode 100644 docs/src/main/paradox/docs/delta/api/assets/jira/request-token.sh delete mode 100644 docs/src/main/paradox/docs/delta/api/jira.md diff --git a/build.sbt b/build.sbt index b6ebc9d949..19b0e9df70 100755 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,6 @@ val distageVersion = "1.2.15" val doobieVersion = "1.0.0-RC6" val fs2Version = "3.11.0" val fs2AwsVersion = "6.1.3" -val googleAuthClientVersion = "1.36.0" val handleBarsVersion = "4.4.0" val hikariVersion = "6.2.1" val jenaVersion = "4.10.0" @@ -97,7 +96,6 @@ lazy val fs2ReactiveStreams = "co.fs2" %% "fs2- lazy val fs2io = "co.fs2" %% "fs2-io" % fs2Version lazy val fs2Aws = "io.laserdisc" %% "fs2-aws-core" % fs2AwsVersion lazy val fs2AwsS3 = "io.laserdisc" %% "fs2-aws-s3" % fs2AwsVersion -lazy val googleAuthClient = "com.google.oauth-client" % "google-oauth-client" % googleAuthClientVersion lazy val handleBars = "com.github.jknack" % "handlebars" % handleBarsVersion lazy val jenaArq = "org.apache.jena" % "jena-arq" % jenaVersion exclude ("com.apicatalog", "titanium-json-ld") lazy val jsonldjava = "com.github.jsonld-java" % "jsonld-java" % jsonldjavaVersion @@ -357,7 +355,6 @@ lazy val app = project val compositeViewsFile = (compositeViewsPlugin / assembly).value val searchFile = (searchPlugin / assembly).value val projectDeletionFile = (projectDeletionPlugin / assembly).value - val jiraFile = (jiraPlugin / assembly).value val pluginsTarget = target.value / "plugins" IO.createDirectory(pluginsTarget) IO.copy( @@ -369,8 +366,7 @@ lazy val app = project archiveFile -> (pluginsTarget / archiveFile.getName), compositeViewsFile -> (pluginsTarget / compositeViewsFile.getName), searchFile -> (pluginsTarget / searchFile.getName), - projectDeletionFile -> (pluginsTarget / projectDeletionFile.getName), - jiraFile -> (pluginsTarget / jiraFile.getName) + projectDeletionFile -> (pluginsTarget / projectDeletionFile.getName) ) ) }, @@ -396,7 +392,6 @@ lazy val app = project val compositeViewsFile = (compositeViewsPlugin / assembly).value val searchFile = (searchPlugin / assembly).value val projectDeletionFile = (projectDeletionPlugin / assembly).value - val jiraFile = (jiraPlugin / assembly).value Seq( (esFile, "plugins/" + esFile.getName), (bgFile, "plugins/" + bgFile.getName), @@ -405,7 +400,6 @@ lazy val app = project (archiveFile, "plugins/" + archiveFile.getName), (compositeViewsFile, "plugins/" + compositeViewsFile.getName), (searchFile, "plugins/" + searchFile.getName), - (jiraFile, "plugins/" + jiraFile.getName), (projectDeletionFile, "plugins/disabled/" + projectDeletionFile.getName) ) } @@ -651,31 +645,6 @@ lazy val graphAnalyticsPlugin = project coverageFailOnMinimum := false ) -lazy val jiraPlugin = project - .in(file("delta/plugins/jira")) - .enablePlugins(BuildInfoPlugin) - .settings(shared, compilation, assertJavaVersion, discardModuleInfoAssemblySettings, coverage, release) - .dependsOn( - sdk % "provided;test->test" - ) - .settings( - name := "delta-jira-plugin", - moduleName := "delta-jira-plugin", - libraryDependencies ++= Seq( - googleAuthClient, - kamonAkkaHttp % Provided - ), - buildInfoKeys := Seq[BuildInfoKey](version), - buildInfoPackage := "ch.epfl.bluebrain.nexus.delta.plugins.jira", - addCompilerPlugin(betterMonadicFor), - assembly / assemblyJarName := "jira.jar", - assembly / assemblyOption := (assembly / assemblyOption).value.withIncludeScala(false), - assembly / test := {}, - addArtifact(Artifact("jira", "plugin"), assembly), - Test / fork := true, - coverageFailOnMinimum := false - ) - lazy val plugins = project .in(file("delta/plugins")) .settings(shared, compilation, noPublish) @@ -688,8 +657,7 @@ lazy val plugins = project archivePlugin, projectDeletionPlugin, testPlugin, - graphAnalyticsPlugin, - jiraPlugin + graphAnalyticsPlugin ) lazy val delta = project diff --git a/delta/plugins/jira/src/main/resources/jira.conf b/delta/plugins/jira/src/main/resources/jira.conf deleted file mode 100644 index 5c2564b133..0000000000 --- a/delta/plugins/jira/src/main/resources/jira.conf +++ /dev/null @@ -1,16 +0,0 @@ -plugins.jira { - # set to false to skip loading the plugin - enabled = false - # the priority of the plugin - priority = 10 - # the base uri to Jira - base = "http://localhost:9091" - # See https://developer.atlassian.com/server/jira/platform/oauth/ - # on how to set up OAuth in Jira - # the consumer key for the Oauth configuration - consumer-key = "Nexus" - # the secret for the Oauth configuration - secret = "CHANGEME" - # the private key for the Oauth configuration in PCKS8 format - private-key = "CHANGEME" -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraClient.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraClient.scala deleted file mode 100644 index 22fa18729c..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraClient.scala +++ /dev/null @@ -1,237 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import akka.http.scaladsl.model.Uri -import cats.effect.IO -import cats.syntax.all._ -import ch.epfl.bluebrain.nexus.delta.kernel.Logger -import ch.epfl.bluebrain.nexus.delta.plugins.jira.JiraError.{AccessTokenExpected, NoTokenError, RequestTokenExpected} -import ch.epfl.bluebrain.nexus.delta.plugins.jira.OAuthToken.{AccessToken, RequestToken} -import ch.epfl.bluebrain.nexus.delta.plugins.jira.config.JiraConfig -import ch.epfl.bluebrain.nexus.delta.plugins.jira.model.{AuthenticationRequest, JiraResponse, Verifier} -import ch.epfl.bluebrain.nexus.delta.rdf.syntax.uriSyntax -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User -import com.google.api.client.auth.oauth.{OAuthAuthorizeTemporaryTokenUrl, OAuthGetAccessToken, OAuthGetTemporaryToken, OAuthRsaSigner} -import com.google.api.client.http.javanet.NetHttpTransport -import com.google.api.client.http.{ByteArrayContent, GenericUrl} -import io.circe.JsonObject -import io.circe.syntax.EncoderOps -import org.apache.commons.codec.binary.Base64 - -import java.nio.charset.StandardCharsets -import java.security.KeyFactory -import java.security.spec.PKCS8EncodedKeySpec - -/** - * Client that allows to authorize Delta to query Jira on behalf on the users by handling the Oauth authorization flow - * and passing by the queries to Jira - */ -trait JiraClient { - - /** - * Creates an authorization request for the current user - */ - def requestToken()(implicit caller: User): IO[AuthenticationRequest] - - /** - * Generates an access token for the current user by providing the verifier code provided by the user - */ - def accessToken(verifier: Verifier)(implicit caller: User): IO[Unit] - - /** - * Create an issue on behalf of the user in Jira - * @param payload - * the issue payload - */ - def createIssue(payload: JsonObject)(implicit caller: User): IO[JiraResponse] - - /** - * Edits an issue on behalf of the user in Jira - * @param payload - * the issue payload - */ - def editIssue(issueId: String, payload: JsonObject)(implicit caller: User): IO[JiraResponse] - - /** - * Get the issue matching the provided identifier - * @param issueId - * the identifier - */ - def getIssue(issueId: String)(implicit caller: User): IO[JiraResponse] - - /** - * List the projects the current user has access to - * @param recent - * when provided, return the n most recent projects the user was active in - */ - def listProjects(recent: Option[Int])(implicit caller: User): IO[JiraResponse] - - /** - * Search issues in Jira the user has access to according to the provided search payload - * @param payload - * the search payload - */ - def search(payload: JsonObject)(implicit caller: User): IO[JiraResponse] - -} - -object JiraClient { - - private val logger = Logger[JiraClient] - private val accessTokenUrl = Uri.Path("/plugins/servlet/oauth/access-token") - private val authorizationUrl = Uri.Path("/plugins/servlet/oauth/authorize") - private val requestTokenUrl = Uri.Path("/plugins/servlet/oauth/request-token") - - private val issueUrl = Uri.Path("/rest/api/2/issue") - private val projectUrl = Uri.Path("/rest/api/2/project") - private val searchUrl = Uri.Path("/rest/api/2/search") - - private class JiraOAuthGetTemporaryToken(jiraBase: Uri) - extends OAuthGetTemporaryToken((jiraBase / requestTokenUrl).toString()) { - this.usePost = true - } - - private class JiraOAuthGetAccessToken(jiraBase: Uri) - extends OAuthGetAccessToken((jiraBase / accessTokenUrl).toString()) { - this.usePost = true - } - - /** - * Creates the Jira client - * @param store - * the token store - * @param jiraConfig - * the jira configuration - */ - def apply(store: TokenStore, jiraConfig: JiraConfig): IO[JiraClient] = { - IO { - // Create the RSA signer according to the PKCS8 key provided by the configuration - val privateBytes = Base64.decodeBase64(jiraConfig.privateKey.value) - val keySpec = new PKCS8EncodedKeySpec(privateBytes) - val kf = KeyFactory.getInstance("RSA") - val signer = new OAuthRsaSigner() - signer.privateKey = kf.generatePrivate(keySpec) - signer - } - .map { signer => - new JiraClient { - - private val netHttpTransport = new NetHttpTransport() - - override def requestToken()(implicit caller: User): IO[AuthenticationRequest] = { - val tempToken = new JiraOAuthGetTemporaryToken(jiraConfig.base) - tempToken.consumerKey = jiraConfig.consumerKey - tempToken.signer = signer - tempToken.transport = netHttpTransport - tempToken.callback = "oob" - for { - token <- IO.blocking { tempToken.execute() }.map(_.token) - _ <- logger.debug(s"Request Token value: $token") - _ <- store.save(caller, RequestToken(token)) - } yield { - val authorizationURL = - new OAuthAuthorizeTemporaryTokenUrl((jiraConfig.base / authorizationUrl).toString()) - authorizationURL.temporaryToken = token - AuthenticationRequest(Uri(authorizationURL.toString)) - } - }.adaptError { e => JiraError.from(e) } - - override def accessToken(verifier: Verifier)(implicit caller: User): IO[Unit] = { - for { - tokenOpt <- store.get(caller) - token <- tokenOpt match { - case None => IO.raiseError(NoTokenError) - case Some(_: AccessToken) => IO.raiseError(RequestTokenExpected) - case Some(RequestToken(value)) => - val accessToken = new JiraOAuthGetAccessToken(jiraConfig.base) - accessToken.consumerKey = jiraConfig.consumerKey - accessToken.signer = signer - accessToken.transport = netHttpTransport - accessToken.verifier = verifier.value - accessToken.temporaryToken = value - IO.blocking { - accessToken.execute().token - } - } - _ <- store.save(caller, AccessToken(token)) - _ <- logger.debug("Access Token:" + token) - } yield () - }.adaptError { e => JiraError.from(e) } - - override def createIssue(payload: JsonObject)(implicit caller: User): IO[JiraResponse] = - requestFactory(caller).flatMap { factory => - val url = jiraConfig.base / issueUrl - JiraResponse( - factory.buildPostRequest( - new GenericUrl(url.toString()), - jsonContent(payload) - ) - ) - } - - override def editIssue(issueId: String, payload: JsonObject)(implicit - caller: User - ): IO[JiraResponse] = - requestFactory(caller).flatMap { factory => - val url = jiraConfig.base / issueUrl / issueId - JiraResponse( - factory.buildPutRequest( - new GenericUrl(url.toString()), - jsonContent(payload) - ) - ) - } - - override def getIssue(issueId: String)(implicit caller: User): IO[JiraResponse] = - requestFactory(caller).flatMap { factory => - val url = jiraConfig.base / issueUrl / issueId - JiraResponse( - factory.buildGetRequest( - new GenericUrl(url.toString()) - ) - ) - } - - override def listProjects(recent: Option[Int])(implicit caller: User): IO[JiraResponse] = - requestFactory(caller).flatMap { factory => - val url = recent.fold(jiraConfig.base / projectUrl) { r => - (jiraConfig.base / projectUrl).withQuery(Uri.Query("recent" -> r.toString)) - } - JiraResponse( - factory.buildGetRequest( - new GenericUrl(url.toString()) - ) - ) - } - - def search(payload: JsonObject)(implicit caller: User): IO[JiraResponse] = - requestFactory(caller).flatMap { factory => - JiraResponse( - factory.buildPostRequest( - new GenericUrl((jiraConfig.base / searchUrl).toString()), - jsonContent(payload) - ) - ) - } - - private def requestFactory(caller: User) = store.get(caller).flatMap { - case None => IO.raiseError(NoTokenError) - case Some(_: RequestToken) => IO.raiseError(AccessTokenExpected) - case Some(AccessToken(token)) => - IO.pure { - val accessToken = new JiraOAuthGetAccessToken(jiraConfig.base) - accessToken.consumerKey = jiraConfig.consumerKey - accessToken.signer = signer - accessToken.transport = netHttpTransport - accessToken.verifier = jiraConfig.secret.value - accessToken.temporaryToken = token - netHttpTransport.createRequestFactory(accessToken.createParameters()) - } - } - - private def jsonContent(payload: JsonObject) = - new ByteArrayContent("application/json", payload.asJson.noSpaces.getBytes(StandardCharsets.UTF_8)) - } - } - } - -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraError.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraError.scala deleted file mode 100644 index 1449fc4be9..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraError.scala +++ /dev/null @@ -1,82 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import akka.http.scaladsl.model.StatusCodes -import ch.epfl.bluebrain.nexus.delta.kernel.utils.ClassUtils -import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.contexts -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.ContextValue -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder -import ch.epfl.bluebrain.nexus.delta.sdk.error.SDKError -import ch.epfl.bluebrain.nexus.delta.sdk.marshalling.HttpResponseFields -import com.google.api.client.http.HttpResponseException -import io.circe.syntax.EncoderOps -import io.circe.{parser, Encoder, Json, JsonObject} - -/** - * OAuth related errors when exchanging with Jira - */ -sealed abstract class JiraError(reason: String, details: Option[String] = None) extends SDKError { - final override def getMessage: String = details.fold(reason)(d => s"$reason\nDetails: $d") -} - -object JiraError { - - /** - * No token has been found for the current user - */ - final case object NoTokenError extends JiraError("No token has been found for the current user.") - - /** - * A request token was expected for the current user - */ - final case object RequestTokenExpected extends JiraError("A request token was expected for the current user.") - - /** - * An access token was expected for the current user - */ - final case object AccessTokenExpected extends JiraError("An access token was expected for the current user.") - - /** - * An error when interacting with Jira after the authentication process - */ - final case class JiraResponseError(statusCode: Int, content: Json) - extends JiraError(s"Jira responded with an error with status code $statusCode") - - /** - * Unexpected error - */ - final case class UnexpectedError(reason: String, details: Option[String] = None) extends JiraError(reason, details) - - /** - * Create a Jira error from the given exception - */ - def from(e: Throwable): JiraError = - e match { - case httpException: HttpResponseException => - JiraResponseError( - httpException.getStatusCode, - parser.parse(httpException.getContent).getOrElse(Json.fromString(httpException.getContent)) - ) - case _ => - UnexpectedError(e.getMessage) - } - - implicit val jiraErrorEncoder: Encoder.AsObject[JiraError] = - Encoder.AsObject.instance { e => - val tpe = ClassUtils.simpleName(e) - val default = JsonObject.empty.add(keywords.tpe, tpe.asJson).add("reason", e.getMessage.asJson) - e match { - case JiraResponseError(_, content) => default.add("content", content) - case _ => default - } - } - - implicit final val jiraErrorJsonLdEncoder: JsonLdEncoder[JiraError] = - JsonLdEncoder.computeFromCirce(ContextValue(contexts.error)) - - implicit val jiraErrorHttpResponseFields: HttpResponseFields[JiraError] = - HttpResponseFields { - case JiraResponseError(statusCode, _) => StatusCodes.getForKey(statusCode).getOrElse(StatusCodes.BadRequest) - case _ => StatusCodes.BadRequest - } -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPlugin.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPlugin.scala deleted file mode 100644 index 133c75375d..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPlugin.scala +++ /dev/null @@ -1,9 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import cats.effect.IO -import ch.epfl.bluebrain.nexus.delta.sdk.plugin.Plugin - -object JiraPlugin extends Plugin { - - override def stop(): IO[Unit] = IO.unit -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginDef.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginDef.scala deleted file mode 100644 index f1de6d030a..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginDef.scala +++ /dev/null @@ -1,17 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import cats.effect.IO -import ch.epfl.bluebrain.nexus.delta.kernel.dependency.ComponentDescription.PluginDescription -import ch.epfl.bluebrain.nexus.delta.sdk.plugin.{Plugin, PluginDef} -import izumi.distage.model.Locator -import izumi.distage.model.definition.ModuleDef - -class JiraPluginDef extends PluginDef { - - override def module: ModuleDef = new JiraPluginModule(priority) - - override val info: PluginDescription = PluginDescription("jira", BuildInfo.version) - - override def initialize(locator: Locator): IO[Plugin] = IO.pure(JiraPlugin) - -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginModule.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginModule.scala deleted file mode 100644 index 5705db2404..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/JiraPluginModule.scala +++ /dev/null @@ -1,50 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import cats.effect.{Clock, IO} -import ch.epfl.bluebrain.nexus.delta.plugins.jira.config.JiraConfig -import ch.epfl.bluebrain.nexus.delta.plugins.jira.routes.JiraRoutes -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution -import ch.epfl.bluebrain.nexus.delta.rdf.utils.JsonKeyOrdering -import ch.epfl.bluebrain.nexus.delta.sdk._ -import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck -import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities -import ch.epfl.bluebrain.nexus.delta.sdk.model._ -import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors -import izumi.distage.model.definition.{Id, ModuleDef} - -/** - * Jira plugin wiring. - */ -class JiraPluginModule(priority: Int) extends ModuleDef { - - make[JiraConfig].from { JiraConfig.load(_) } - - make[JiraClient].fromEffect { (xas: Transactors, jiraConfig: JiraConfig, clock: Clock[IO]) => - JiraClient(TokenStore(xas, clock), jiraConfig) - } - - make[JiraRoutes].from { - ( - identities: Identities, - aclCheck: AclCheck, - jiraClient: JiraClient, - baseUri: BaseUri, - cr: RemoteContextResolution @Id("aggregate"), - ordering: JsonKeyOrdering - ) => - new JiraRoutes( - identities, - aclCheck, - jiraClient - )( - baseUri, - cr, - ordering - ) - } - - many[PriorityRoute].add { (route: JiraRoutes) => - PriorityRoute(priority, route.routes, requiresStrictEntity = true) - } - -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/OAuthToken.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/OAuthToken.scala deleted file mode 100644 index 78c3ffb401..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/OAuthToken.scala +++ /dev/null @@ -1,46 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import io.circe.Codec -import io.circe.generic.extras.Configuration -import io.circe.generic.extras.semiauto.deriveConfiguredCodec - -/** - * OAuth token provided by the Jira instance - * - * In Jira, a client is authenticated as the user involved in the OAuth dance and is authorized to have read and write - * access as that user. The data that can be retrieved and changed by the client is controlled by the user's - * permissions in Jira. The authorization process works by getting the resource owner to grant access to their - * information on the resource by authorizing a request token. This request token is used by the consumer to obtain an - * access token from the resource. Once the client has an access token, it can use the access token to make - * authenticated requests to the resource until the token expires or is revoked. - * - * This process can be separated into three stages: - * - The user authorizes the client with Jira to receive an access code. - * - The client makes a request to Jira with the access code and receives an access token. - * - The client can now receive data from Jira when it makes a request including the access token. Note, the client - * can continue making authenticated requests to Jira until the token expires or is revoked. - * - * @see - * https://developer.atlassian.com/server/jira/platform/oauth/ - */ -sealed trait OAuthToken extends Product with Serializable { - def value: String -} - -object OAuthToken { - - /** - * Token issued during the authorization request - */ - final case class RequestToken(value: String) extends OAuthToken - - /** - * Access token to query Jira - */ - final case class AccessToken(value: String) extends OAuthToken - - implicit val oauthTokenCodec: Codec.AsObject[OAuthToken] = { - implicit val cfg: Configuration = Configuration.default - deriveConfiguredCodec[OAuthToken] - } -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStore.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStore.scala deleted file mode 100644 index 5f70fbc3c5..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStore.scala +++ /dev/null @@ -1,64 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import cats.effect.{Clock, IO} -import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors -import ch.epfl.bluebrain.nexus.delta.sourcing.implicits._ -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User -import doobie.syntax.all._ -import doobie.postgres.implicits._ -import io.circe.Json -import io.circe.syntax._ - -/** - * Stores Jira tokens in the underlying databases - */ -trait TokenStore { - - /** - * Retrieves the token for the given user - * @param user - * the user - */ - def get(user: User): IO[Option[OAuthToken]] - - /** - * Save the token for the given user - * @param user - * the user - * @param oauthToken - * the associated token - */ - def save(user: User, oauthToken: OAuthToken): IO[Unit] - -} - -object TokenStore { - - /** - * Create a token store - */ - def apply(xas: Transactors, clock: Clock[IO]): TokenStore = { - new TokenStore { - override def get(user: Identity.User): IO[Option[OAuthToken]] = - sql"SELECT token_value FROM jira_tokens WHERE realm = ${user.realm.value} and subject = ${user.subject}" - .query[Json] - .option - .transact(xas.read) - .flatMap { - case Some(token) => - IO.fromEither(token.as[OAuthToken]).map(Some(_)) - case None => IO.none - } - - override def save(user: Identity.User, oauthToken: OAuthToken): IO[Unit] = - clock.realTimeInstant.flatMap { now => - sql""" INSERT INTO jira_tokens(realm, subject, instant, token_value) - | VALUES(${user.realm.value}, ${user.subject}, $now, ${oauthToken.asJson}) - | ON CONFLICT (realm, subject) DO UPDATE SET instant = EXCLUDED.instant, token_value = EXCLUDED.token_value - """.stripMargin.update.run.transact(xas.write).void - } - } - } - -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/config/JiraConfig.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/config/JiraConfig.scala deleted file mode 100644 index 3b02852cd0..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/config/JiraConfig.scala +++ /dev/null @@ -1,39 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira.config - -import akka.http.scaladsl.model.Uri -import ch.epfl.bluebrain.nexus.delta.sdk.instances._ -import ch.epfl.bluebrain.nexus.delta.kernel.Secret -import com.typesafe.config.Config -import pureconfig.generic.semiauto.deriveReader -import pureconfig.{ConfigReader, ConfigSource} - -/** - * Jira plugin configuration - * @param base - * the base url of Jira - * @param consumerKey - * the consumer key for the OAuth configuration - * @param secret - * the secret for the OAuth configuration - * @param privateKey - * the private key to sign messages to Jira - */ -final case class JiraConfig( - base: Uri, - consumerKey: String, - secret: Secret[String], - privateKey: Secret[String] -) - -object JiraConfig { - - def load(config: Config): JiraConfig = - ConfigSource - .fromConfig(config) - .at("plugins.jira") - .loadOrThrow[JiraConfig] - - implicit final val jiraConfigReader: ConfigReader[JiraConfig] = - deriveReader[JiraConfig] - -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/AuthenticationRequest.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/AuthenticationRequest.scala deleted file mode 100644 index 258f5fa902..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/AuthenticationRequest.scala +++ /dev/null @@ -1,21 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira.model - -import akka.http.scaladsl.model.Uri -import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ -import io.circe.Encoder -import io.circe.generic.extras.Configuration -import io.circe.generic.extras.semiauto.deriveConfiguredEncoder - -/** - * The request sent back to the user so that he can authorize the client - * @param value - * the Jira uri so that the user can approve - */ -final case class AuthenticationRequest(value: Uri) - -object AuthenticationRequest { - - implicit private val configuration: Configuration = Configuration.default.withStrictDecoding - implicit val authenticationRequestDecoder: Encoder[AuthenticationRequest] = - deriveConfiguredEncoder[AuthenticationRequest] -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/JiraResponse.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/JiraResponse.scala deleted file mode 100644 index 7fbf7c2c07..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/JiraResponse.scala +++ /dev/null @@ -1,28 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira.model - -import cats.effect.IO -import cats.syntax.all._ -import ch.epfl.bluebrain.nexus.delta.plugins.jira.JiraError -import com.google.api.client.http.HttpRequest -import io.circe.{parser, Json} - -/** - * Jira response - */ -final case class JiraResponse(content: Option[Json]) - -object JiraResponse { - - def apply(request: HttpRequest): IO[JiraResponse] = { - IO.blocking(request.execute()) - .flatMap { response => - val content = response.parseAsString() - if (content.nonEmpty) { - IO.fromEither(parser.parse(content)).map { r => JiraResponse(Some(r)) } - } else { - IO.pure(JiraResponse(None)) - } - } - .adaptError { e => JiraError.from(e) } - } -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/Verifier.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/Verifier.scala deleted file mode 100644 index 82f2ad37e6..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/model/Verifier.scala +++ /dev/null @@ -1,16 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira.model - -import io.circe.Decoder -import io.circe.generic.extras.Configuration -import io.circe.generic.extras.semiauto.deriveConfiguredDecoder - -/** - * The verifier code provided by the user allowing Delta to retrieve an access token for this user - */ -final case class Verifier(value: String) - -object Verifier { - - implicit private val configuration: Configuration = Configuration.default.withStrictDecoding - implicit val verificationCodeDecoder: Decoder[Verifier] = deriveConfiguredDecoder[Verifier] -} diff --git a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/routes/JiraRoutes.scala b/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/routes/JiraRoutes.scala deleted file mode 100644 index a586f082ab..0000000000 --- a/delta/plugins/jira/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/routes/JiraRoutes.scala +++ /dev/null @@ -1,104 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira.routes - -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.server.{Directive1, Route} -import cats.effect.IO -import cats.syntax.all._ -import ch.epfl.bluebrain.nexus.delta.plugins.jira.model.{JiraResponse, Verifier} -import ch.epfl.bluebrain.nexus.delta.plugins.jira.{JiraClient, JiraError} -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution -import ch.epfl.bluebrain.nexus.delta.rdf.utils.JsonKeyOrdering -import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck -import ch.epfl.bluebrain.nexus.delta.kernel.circe.CirceUnmarshalling -import ch.epfl.bluebrain.nexus.delta.sdk.directives.AuthDirectives -import ch.epfl.bluebrain.nexus.delta.sdk.directives.DeltaDirectives._ -import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.AuthorizationFailed -import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities -import ch.epfl.bluebrain.nexus.delta.sdk.marshalling.RdfMarshalling -import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri -import ch.epfl.bluebrain.nexus.delta.sdk.realms.model.RealmRejection -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User -import io.circe.JsonObject -import io.circe.syntax.EncoderOps - -/** - * The Jira routes. - * - * @param identities - * the identity module - * @param aclCheck - * to check acls - * @param jiraClient - * the jira client - */ -class JiraRoutes( - identities: Identities, - aclCheck: AclCheck, - jiraClient: JiraClient -)(implicit baseUri: BaseUri, cr: RemoteContextResolution, ordering: JsonKeyOrdering) - extends AuthDirectives(identities, aclCheck) - with CirceUnmarshalling - with RdfMarshalling { - - private def extractUser: Directive1[User] = extractCaller.flatMap { - _.subject match { - case u: User => provide(u) - case _ => - extractRequest.flatMap { request => - failWith(AuthorizationFailed(request)) - } - } - } - - private def adaptResponse(io: IO[JiraResponse]) = - io.map(_.content).attemptNarrow[JiraError] - - def routes: Route = - baseUriPrefix(baseUri.prefix) { - pathPrefix("jira") { - extractUser { implicit user => - concat( - // Request token - (pathPrefix("request-token") & post & pathEndOrSingleSlash) { - emit(jiraClient.requestToken().map(_.asJson).attemptNarrow[RealmRejection]) - }, - // Get the access token - (pathPrefix("access-token") & post & pathEndOrSingleSlash) { - entity(as[Verifier]) { verifier => - emit(jiraClient.accessToken(verifier).attemptNarrow[RealmRejection]) - } - }, - // Issues - pathPrefix("issue") { - concat( - // Create an issue - (post & entity(as[JsonObject])) { payload => - emit(StatusCodes.Created, adaptResponse(jiraClient.createIssue(payload))) - }, - // Edit an issue - (put & pathPrefix(Segment)) { issueId => - entity(as[JsonObject]) { payload => - emit(StatusCodes.NoContent, adaptResponse(jiraClient.editIssue(issueId, payload))) - } - }, - // Get an issue - (get & pathPrefix(Segment)) { issueId => - emit(adaptResponse(jiraClient.getIssue(issueId))) - } - ) - }, - // List projects - (get & pathPrefix("project") & get & parameter("recent".as[Int].?)) { recent => - emit(adaptResponse(jiraClient.listProjects(recent))) - }, - // Search issues - (post & pathPrefix("search") & pathEndOrSingleSlash) { - entity(as[JsonObject]) { payload => - emit(adaptResponse(jiraClient.search(payload))) - } - } - ) - } - } - } -} diff --git a/delta/plugins/jira/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStoreSuite.scala b/delta/plugins/jira/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStoreSuite.scala deleted file mode 100644 index dd08d0f53f..0000000000 --- a/delta/plugins/jira/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/jira/TokenStoreSuite.scala +++ /dev/null @@ -1,41 +0,0 @@ -package ch.epfl.bluebrain.nexus.delta.plugins.jira - -import ch.epfl.bluebrain.nexus.delta.plugins.jira.OAuthToken.{AccessToken, RequestToken} -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Label -import ch.epfl.bluebrain.nexus.delta.sourcing.postgres.Doobie -import ch.epfl.bluebrain.nexus.testkit.mu.NexusSuite -import munit.AnyFixture - -class TokenStoreSuite extends NexusSuite with Doobie.Fixture { - - override def munitFixtures: Seq[AnyFixture[_]] = List(doobie) - - private lazy val xas = doobie() - - private lazy val tokenStore: TokenStore = TokenStore(xas, clock) - - private val user = User("Alice", Label.unsafe("Wonderland")) - - private val request = RequestToken("request") - private val access = AccessToken("access") - - test("Return none if no token exist for the user") { - tokenStore.get(user).assertEquals(None) - } - - test("Save a given token for the user and return it") { - for { - _ <- tokenStore.save(user, request) - _ <- tokenStore.get(user).assertEquals(Some(request)) - } yield () - } - - test("Overwrite an existing token for the user") { - for { - _ <- tokenStore.save(user, access) - _ <- tokenStore.get(user).assertEquals(Some(access)) - } yield () - } - -} diff --git a/delta/sourcing-psql/src/main/resources/scripts/postgres/drop/drop-tables.ddl b/delta/sourcing-psql/src/main/resources/scripts/postgres/drop/drop-tables.ddl index 795658cb74..de82c68b2b 100644 --- a/delta/sourcing-psql/src/main/resources/scripts/postgres/drop/drop-tables.ddl +++ b/delta/sourcing-psql/src/main/resources/scripts/postgres/drop/drop-tables.ddl @@ -15,6 +15,5 @@ DROP TABLE IF EXISTS public.projection_offsets; DROP TABLE IF EXISTS public.failed_elem_logs; DROP TABLE IF EXISTS public.deleted_project_reports; DROP TABLE IF EXISTS public.blazegraph_queries; -DROP TABLE IF EXISTS public.jira_tokens; DROP SEQUENCE IF EXISTS public.event_offset; DROP SEQUENCE IF EXISTS public.state_offset; \ No newline at end of file diff --git a/delta/sourcing-psql/src/main/resources/scripts/postgres/init/V1_11_M07_001__jira_drop_jira_tokens.ddl b/delta/sourcing-psql/src/main/resources/scripts/postgres/init/V1_11_M07_001__jira_drop_jira_tokens.ddl new file mode 100644 index 0000000000..d2ca6394fa --- /dev/null +++ b/delta/sourcing-psql/src/main/resources/scripts/postgres/init/V1_11_M07_001__jira_drop_jira_tokens.ddl @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS public.jira_tokens; + diff --git a/docs/src/main/paradox/docs/delta/api/assets/jira/access-token.sh b/docs/src/main/paradox/docs/delta/api/assets/jira/access-token.sh deleted file mode 100644 index 5ebe99b590..0000000000 --- a/docs/src/main/paradox/docs/delta/api/assets/jira/access-token.sh +++ /dev/null @@ -1,4 +0,0 @@ -curl -X POST "http://localhost:8080/v1/jira/request-token" -d \ - '{ - "value": "MY_CODE" - }' \ No newline at end of file diff --git a/docs/src/main/paradox/docs/delta/api/assets/jira/request-token-response.json b/docs/src/main/paradox/docs/delta/api/assets/jira/request-token-response.json deleted file mode 100644 index a4038cff01..0000000000 --- a/docs/src/main/paradox/docs/delta/api/assets/jira/request-token-response.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "value": "https://my-jira-instance/plugins/servlet/oauth/authorize?oauth_token=TEMP_TOKEN" -} \ No newline at end of file diff --git a/docs/src/main/paradox/docs/delta/api/assets/jira/request-token.sh b/docs/src/main/paradox/docs/delta/api/assets/jira/request-token.sh deleted file mode 100644 index 6499cf11e4..0000000000 --- a/docs/src/main/paradox/docs/delta/api/assets/jira/request-token.sh +++ /dev/null @@ -1 +0,0 @@ -curl -X POST "http://localhost:8080/v1/jira/request-token" \ No newline at end of file diff --git a/docs/src/main/paradox/docs/delta/api/index.md b/docs/src/main/paradox/docs/delta/api/index.md index f459528312..4f64dcb4cd 100644 --- a/docs/src/main/paradox/docs/delta/api/index.md +++ b/docs/src/main/paradox/docs/delta/api/index.md @@ -26,7 +26,6 @@ * @ref:[Archives](archives-api.md) * @ref:[Search](search-api.md) * @ref:[Graph Analytics](graph-analytics-api.md) -* @ref:[Jira integration](jira.md) * @ref:[Supervision](supervision-api.md) * @ref:[Type Hierarchy](type-hierarchy-api.md) diff --git a/docs/src/main/paradox/docs/delta/api/jira.md b/docs/src/main/paradox/docs/delta/api/jira.md deleted file mode 100644 index abb2505d9e..0000000000 --- a/docs/src/main/paradox/docs/delta/api/jira.md +++ /dev/null @@ -1,93 +0,0 @@ -# Jira integration - -The Jira integration plugin comes from the need at BBP to have the Nexus platform and Jira to interact by linking Nexus resources -and Jira issues. - -The @link:[Jira server](https://developer.atlassian.com/server/jira/platform/getting-started/) API only allows to authenticate -clients using the OAuth 1.0a protocol. This implies some set-up on the server-side so a minimal plugin was introduced in Delta to handle -the authorization flow and to expose the Jira API operations needed by @ref[Nexus fusion](../../fusion/index.md) to link Nexus resources -and Jira issues. - -A guide introducing OAuth and how to integrate with Jira using it with more details is available @link[on the Atlassian website](https://developer.atlassian.com/server/jira/platform/oauth/) - -@@@ note { .tip title="Authorization notes" } - -Like the other endpoints in Delta, clients still have to present a valid token. - -To have access to the different endpoints provided by this plugin, the user must first grant Delta to access to their information and to act on their -behalf as defined by the authorization workflow presented @link:[here](https://developer.atlassian.com/server/jira/platform/oauth/#authorization-flow). - -As Nexus impersonates the user when interacting with Jira, it will do it with the same permissions as the user. -Nexus does not perform any additional permission checks, it relies fully on Jira to perform this task. - -@@@ - -## Request token - -Starts the authorization process by obtaining a request token from Jira which will return a link so that the user can grant permission to Nexus to -access Jira on their behalf. - -Nexus passes along the link to the client. - -``` -POST /v1/jira/request-token -``` - -**Example** - -Request -: @@snip [request-token.sh](assets/jira/request-token.sh) - -Response -: @@snip [request-token-response.json](assets/jira/request-token-response.json) - -## Access token - -After granting access to Delta, the user will get an access code that must be passed to this endpoint so that Delta can finalize the authorization flow -and get an access token so that it can request and receive data from Jira. - -``` -POST /v1/jira/access-token -``` - -**Example** - -Request -: @@snip [access-token.sh](assets/jira/access-token.sh) - -An empty resource is returned for this endpoint. - -## Create an issue - -``` -POST /v1/jira/issue -``` - -The request and response are the same as those described for the @link:[create issue endpoint](https://docs.atlassian.com/software/jira/docs/api/REST/8.22.2/#issue-createIssue) -in the Jira Reference except for the `updateHistory` parameter which is not available in the Nexus endpoint. - -## Edit an issue - -``` -PUT /v1/jira/issue/{issueId} -``` - -The request and response are the same as those described for the @link:[edit issue endpoint](https://docs.atlassian.com/software/jira/docs/api/REST/8.22.2/#issue-editIssue) -in the Jira Reference except for the `notifyUsers` parameter which is not available in the Nexus endpoint - -## List available projects - -``` -GET /v1/jira/project?recent={number} -``` - -The request and response are the same as those described for the @link:[list projects endpoint](https://docs.atlassian.com/software/jira/docs/api/REST/8.22.2/#project-getAllProjects) -in the Jira Reference except for the `expand`, `includeArchived` and `browseArchive` parameters which are not available in the Nexus endpoint. - -## Search issues - -``` -POST /v1/jira/search -``` - -The request and response are the same as those described @link:[search endpoint](https://docs.atlassian.com/software/jira/docs/api/REST/8.22.2/#search-searchUsingSearchRequest) in the Jira Reference. \ No newline at end of file diff --git a/docs/src/main/paradox/docs/delta/plugins/index.md b/docs/src/main/paradox/docs/delta/plugins/index.md index d7c4ec902b..d17b100d3b 100644 --- a/docs/src/main/paradox/docs/delta/plugins/index.md +++ b/docs/src/main/paradox/docs/delta/plugins/index.md @@ -123,7 +123,6 @@ Currently, following Delta functionality is provided by plugins: - files and storages @ref:[API Reference](../api/files-api.md) | @link:[code](https://github.com/BlueBrain/nexus/tree/$git.branch$/delta/plugins/storage/src){ open=new } - global search @ref:[API Reference](../api/search-api.md) | @link:[code](https://github.com/BlueBrain/nexus/tree/$git.branch$/delta/plugins/search/src){ open=new } - graph analytics @ref:[API Reference](../api/graph-analytics-api.md) | @link:[code](https://github.com/BlueBrain/nexus/tree/$git.branch$/delta/plugins/graph-analytics/src){ open=new } -- Jira @ref:[API Reference](../api/jira.md) | @link:[code](https://github.com/BlueBrain/nexus/tree/$git.branch$/delta/plugins/jira/src){ open=new } Elasticsearch plugin is required in order to provide listings in the API, other plugins can be excluded if their functionality is not needed. All the above plugins are included in the Delta @link:[Docker image](https://hub.docker.com/r/bluebrain/nexus-delta/){ open=new }. diff --git a/docs/src/main/paradox/docs/releases/index.md b/docs/src/main/paradox/docs/releases/index.md index 89124b2d96..42d5c3c91a 100644 --- a/docs/src/main/paradox/docs/releases/index.md +++ b/docs/src/main/paradox/docs/releases/index.md @@ -33,7 +33,7 @@ The latest stable release is **v1.10.0** released on **17.09.2024**. The items listed below are changes that have been made in this release that break compatibility with previous releases. - The Remote storage server implementation has been removed -- +- The Jira integration plugin has been removed. @@@ ### New features/enhancements diff --git a/docs/src/main/paradox/docs/running-nexus/configuration/index.md b/docs/src/main/paradox/docs/running-nexus/configuration/index.md index cf2cde24c8..836f2c308a 100644 --- a/docs/src/main/paradox/docs/running-nexus/configuration/index.md +++ b/docs/src/main/paradox/docs/running-nexus/configuration/index.md @@ -306,13 +306,6 @@ To generate such a key in the correct format follow these steps: The archive plugin configuration can be found @link:[here](https://github.com/BlueBrain/nexus/blob/$git.branch$/delta/plugins/archive/src/main/resources/archive.conf){ open=new }. -### Jira plugin configuration - -The Jira plugin configuration can be found @link:[here](https://github.com/BlueBrain/nexus/blob/$git.branch$/delta/plugins/jira/src/main/resources/jira.conf){ open=new }. - -Setting up the Jira plugin needs to set up the endpoint of the Jira instance but also the consumer key, the consumer secret -and the private key required to interact with Jira (more details including the configuration steps in Jira @link:[here](https://developer.atlassian.com/server/jira/platform/oauth/#step-1--configure-jira)). - ## Monitoring For monitoring, Nexus Delta relies on @link:[Kamon](https://kamon.io/){ open=new }.