Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ class AtlasHttpClient(private val appLink: ApplicationLink) : BaseHttpClient() {
val file = tempFileWithData(filename, inputStream)
val filePart = RequestFilePart(mimeType, filename, file, "file")
request.setFiles(listOf(filePart))

request.apply {
setHeader("X-Atlassian-Token", "no-check")
setHeader("Connection", "keep-alive")
setHeader("Cache-Control", "no-cache")
}
request.execute(object : ApplicationLinkResponseHandler<Either<HttpDomainError, HttpResponse<InputStream>>> {
override fun credentialsRequired(response: Response) = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class KtorHttpClient(
}
) {
url("$baseUrl$url")
header("X-Atlassian-Token", "no-check")
header("Connection", "keep-alive")
header("Cache-Control", "no-cache")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ sealed class InsightClientError(
) : AtlassianClientError(error, message, stacktrace, statusCode) {

companion object {
private const val internalErrorString = "Jira/Insight hat ein internes Problem festgestellt"
private const val internalErrorString = "Jira/Assets hat ein internes Problem festgestellt"
fun fromException(e: Throwable): InsightClientError =
ExceptionInsightClientError("Insight-Fehler", e.message ?: internalErrorString, e.stackTraceToString())
ExceptionInsightClientError("Assets-Fehler", e.message ?: internalErrorString, e.stackTraceToString())

fun internalError(message: String): Either<InsightClientError, InsightAttribute> =
InternalInsightClientError("Interner Insight-Fehler", message).asEither()
InternalInsightClientError("Interner Assets-Fehler", message).asEither()

}
}
Expand All @@ -75,16 +75,16 @@ class ObjectNotFoundError(val objectId: InsightObjectId):
class ObjectTypeNotFoundError(val rootObjectTypeId: InsightObjectTypeId) :
InsightClientError("Insight Objekttyp unbekannt", "Der Objekttyp mit der angegebenen InsightObjectTypeId=$rootObjectTypeId wurde nicht gefunden.")

class OtherNotFoundError(message: String) : InsightClientError("Nicht gefunden.", message)
class OtherNotFoundError(message: String) : InsightClientError("Nicht gefunden.", message, statusCode = 404)

open class OtherInsightClientError(error: String, message: String) : InsightClientError(error, message)

/**
* Somewhere inside an HTTP connection failed.
*/
class HttpInsightClientError(statusCode: Int, error: String, message: String) :
class HttpInsightClientError(statusCode: Int, error: String = "Assets-Fehler", message: String) :
InsightClientError(
error = error,
message = "$message StatusCode:$statusCode",
message = "$message (StatusCode:$statusCode)",
statusCode = statusCode
)
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ class HttpInsightAttachmentOperator(private val context: HttpInsightClientContex
override suspend fun downloadAttachmentZip(objectId: InsightObjectId): Either<InsightClientError, InputStream> =
either {
val attachments = getAttachments(objectId).bind()
val fileMap: Map<String, InputStream> = attachments.map { attachment ->
val fileMap: Map<String, InputStream> = attachments.associate { attachment ->
val attachmentContent = downloadAttachment(attachment.url).bind()
attachment.filename to attachmentContent
}.toMap()
}
zipInputStreamForMultipleInputStreams(fileMap).bind()
}

Expand Down
33 changes: 0 additions & 33 deletions kotlin-insight-client/kotlin-insight-client-sdk/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,10 @@
<version>${project.version}</version>
</dependency>

<!-- Kotlin -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>

<!-- Atlassian -->
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -75,31 +68,5 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.atlassian.servicedesk</groupId>
<artifactId>insight-core-model</artifactId>
<version>${insight.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.servicedesk</groupId>
<artifactId>insight-core-persistence</artifactId>
<version>${insight.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>provided</scope>
</dependency>

<!-- Others -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,25 @@ package com.linkedplanet.kotlininsightclient.sdk

import arrow.core.Either
import arrow.core.raise.either
import com.atlassian.jira.security.JiraAuthenticationContext
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.TrustedRequest
import com.atlassian.sal.api.net.TrustedRequestFactory
import com.linkedplanet.kotlininsightclient.api.error.HttpInsightClientError
import com.linkedplanet.kotlininsightclient.api.error.InsightClientError
import com.linkedplanet.kotlininsightclient.api.error.OtherNotFoundError
import com.linkedplanet.kotlininsightclient.api.interfaces.InsightAttachmentOperator
import com.linkedplanet.kotlininsightclient.api.model.AttachmentId
import com.linkedplanet.kotlininsightclient.api.model.InsightAttachment
import com.linkedplanet.kotlininsightclient.api.model.InsightObjectId
import com.linkedplanet.kotlininsightclient.sdk.services.ReverseEngineeredAttachmentUrlResolver
import com.linkedplanet.kotlininsightclient.sdk.services.ReverseEngineeredFileManager
import com.linkedplanet.kotlininsightclient.sdk.util.catchAsInsightClientError
import com.linkedplanet.kotlininsightclient.sdk.util.getComponent
import com.linkedplanet.kotlininsightclient.sdk.util.getOSGiComponent
import com.linkedplanet.kotlininsightclient.sdk.util.toISOString
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade
import com.riadalabs.jira.plugins.insight.services.model.AttachmentBean
import org.apache.http.client.utils.URIBuilder
import java.io.InputStream
import java.io.PipedInputStream
import java.io.PipedOutputStream
Expand All @@ -47,13 +53,12 @@ import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.io.path.createTempFile


object SdkInsightAttachmentOperator : InsightAttachmentOperator {

private val objectFacade: ObjectFacade by getOSGiComponent()

private val fileManager = ReverseEngineeredFileManager()
private val jiraAuthenticationContext: JiraAuthenticationContext by getComponent()
private val attachmentUrlResolver = ReverseEngineeredAttachmentUrlResolver()
private val trustedRequestFactory: TrustedRequestFactory<*> by getOSGiComponent()

override suspend fun getAttachments(objectId: InsightObjectId): Either<InsightClientError, List<InsightAttachment>> =
catchAsInsightClientError {
Expand All @@ -63,27 +68,44 @@ object SdkInsightAttachmentOperator : InsightAttachmentOperator {
}

override suspend fun downloadAttachment(url: String): Either<InsightClientError, InputStream> =
catchAsInsightClientError {
val attachmentId = attachmentUrlResolver.parseAttachmentIdFromPathInformation(url)
val attachmentBean = objectFacade.loadAttachmentBeanById(attachmentId)
fileManager.getObjectAttachmentContent(attachmentBean.objectId, attachmentBean.nameInFileSystem)
}.mapLeft { OtherNotFoundError("Attachment download failed for url:$url") }
either {
val request = trustedGetRequestForCurrentUser(url).bind()
val response = Either.catch { request.executeAndReturn<Response> { it } as Response }
.mapLeft { downloadFailed(500, url) }.bind()
if (!response.isSuccessful) {
raise(downloadFailed(response.statusCode, url))
}
response.responseBodyAsStream
}

private fun downloadFailed(statusCode: Int, url: String, ) = HttpInsightClientError(
statusCode = statusCode,
message = "Anhang Download fehlgeschlagen für URL: $url"
)

private fun trustedGetRequestForCurrentUser(url: String): Either<InsightClientError, TrustedRequest> =
Either.catch {
trustedRequestFactory.createTrustedRequest(Request.MethodType.GET, url).apply {
addTrustedTokenAuthentication(URIBuilder(url).host, jiraAuthenticationContext.loggedInUser.username)
setHeader("Content-Type", "application/json")
}
}.mapLeft {
HttpInsightClientError(
statusCode = 500,
message = "Es konnte keine Verbindung zu Assets erzeugt werden."
)
}

override suspend fun downloadAttachmentZip(objectId: InsightObjectId): Either<InsightClientError, InputStream> =
either {
val fileMap = allAttachmentStreamsForInsightObject(objectId).bind()
val attachments = getAttachments(objectId).bind()
val fileMap: Map<String, InputStream> = attachments.associate { attachment ->
val attachmentContent = downloadAttachment(attachment.url).bind()
attachment.filename to attachmentContent
}
zipInputStreamForMultipleInputStreams(fileMap).bind()
}

private fun allAttachmentStreamsForInsightObject(objectId: InsightObjectId) =
catchAsInsightClientError {
val attachmentBeans = objectFacade.findAttachmentBeans(objectId.raw)
attachmentBeans.map { bean ->
val attachmentContent = fileManager.getObjectAttachmentContent(bean.objectId, bean.nameInFileSystem)
bean.filename to attachmentContent
}.toMap()
}

private fun zipInputStreamForMultipleInputStreams(
fileMap: Map<String, InputStream>
): Either<InsightClientError, InputStream> =
Expand Down Expand Up @@ -112,8 +134,7 @@ object SdkInsightAttachmentOperator : InsightAttachmentOperator {
Files.copy(inputStream, tempFilePath, StandardCopyOption.REPLACE_EXISTING)
val mimeType = URLConnection.guessContentTypeFromName(filename)
val bean = objectFacade.addAttachmentBean(objectId.raw, tempFilePath.toFile(), filename, mimeType, null)
val insightAttachment = beanToInsightAttachment(bean)
insightAttachment
beanToInsightAttachment(bean)
}

override suspend fun deleteAttachment(attachmentId: AttachmentId): Either<InsightClientError, Unit> =
Expand Down

This file was deleted.

5 changes: 5 additions & 0 deletions kotlin-insight-client/kotlin-insight-client-test-base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
<!-- <version>defined by the platform dependency management </version> -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>

</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
package com.linkedplanet.kotlininsightclient

import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.google.gson.Gson
import com.linkedplanet.kotlininsightclient.api.error.AuthenticationError
import com.linkedplanet.kotlininsightclient.api.error.InsightClientError
import org.slf4j.LoggerFactory
import org.http4k.client.Java8HttpClient
import org.http4k.core.Body
import org.http4k.core.HttpHandler
Expand All @@ -36,6 +35,7 @@ import org.http4k.core.then
import org.http4k.filter.ClientFilters
import org.http4k.filter.cookie.BasicCookieStorage
import org.http4k.filter.cookie.CookieStorage
import java.util.*

/**
* Provides access to Jira itself via a logged in AuthenticatedHttpHandler
Expand All @@ -46,31 +46,35 @@ class AuthenticatedJiraHttpClientFactory(
companion object {
data class Credentials(val username: String, val password: String)
}
private val log = LoggerFactory.getLogger(this::class.java)

private val storage: CookieStorage = BasicCookieStorage() // this is just a HashMap
private val httpHandler: HttpHandler = ClientFilters.Cookies(storage = storage).then(Java8HttpClient())
private val gson = Gson()

fun login(credentials: Credentials) : Either<InsightClientError, AuthenticatedHttpHandler> {
fun login(credentials: Credentials): Either<InsightClientError, AuthenticatedHttpHandler> {
val username = credentials.username
val password = credentials.password
val body = gson.toJson(credentials)
val request = Request(Method.POST, "$jiraOrigin/rest/auth/1/session")
.header("content-type", "application/json")
.body(Body(body))

val loginResponse = httpHandler(request)
if (loginResponse.status != Status.OK) {
return AuthenticationError(
"Login failed with HTTP StatusCode:${loginResponse.status.code}"
).left()
} else {
val privateHandler = object : AuthenticatedHttpHandler, HttpHandler by httpHandler {
override fun getWithRelativePath(path: String): Response {
val absolutePath = "$jiraOrigin$path"
return this(Request(Method.GET, absolutePath))
}
log.debug("Continue despite 'session' login failing with HTTP StatusCode:${loginResponse.status.code}")
}
val privateHandler = object : AuthenticatedHttpHandler, HttpHandler by httpHandler {
override fun getWithRelativePath(path: String): Response {
val absolutePath = "$jiraOrigin$path"
val getRequest = Request(Method.GET, absolutePath)
.header(
"Authorization",
"Basic ${Base64.getEncoder().encodeToString("$username:$password".toByteArray())}"
)
return this(getRequest)
}
return privateHandler.right()
}
return privateHandler.right()
}
}

Expand Down
Loading