Skip to content

Commit 2847597

Browse files
authored
Merge pull request #73 from JensHaed/72-Jira-integration-is-incompatible-with-Jira-Cloud
Implemented new Jira connection with PAT authentication and plain jav…
2 parents bf319a5 + 238f7b0 commit 2847597

File tree

10 files changed

+186
-153
lines changed

10 files changed

+186
-153
lines changed

build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ dependencies {
7878
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
7979
implementation("net.engio:mbassador:1.3.2")
8080
implementation("org.controlsfx:controlsfx:11.1.2")
81-
//implementation("net.rcarz:jira-client:0.5")
8281
implementation("com.jsoniter:jsoniter:0.9.23")
8382
implementation(kotlin("stdlib-jdk8"))
8483

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
requires org.controlsfx.controls;
1818
requires org.fxmisc.richtext;
1919
requires org.antlr.antlr4.runtime;
20+
requires java.net.http;
2021

2122
// exporting is needed for javafx to work
2223
opens org.stt;

src/main/kotlin/org/stt/config/JiraConfig.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ package org.stt.config
33
class JiraConfig : ConfigurationContainer {
44
var jiraURI: String? = null
55
var jiraUsername: String? = null
6-
var jiraPassword: PasswordSetting? = null
6+
var jiraToken: PasswordSetting? = null
77
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package org.stt.connector.jira
22

33
class Exceptions(message: String, cause: Throwable) : Exception(message, cause)
4-
class InvalidCredentialsException(message: String, cause: Throwable) : Exception(message, cause)
5-
class IssueDoesNotExistException(message: String, cause: Throwable) : Exception(message, cause)
4+
class InvalidCredentialsException(message: String) : Exception(message)
5+
class IssueDoesNotExistException(message: String) : Exception(message)
6+
class AccessDeniedException(message: String) : Exception(message)
67
class JiraConnectorException(errorMessage: String, cause: Exception) : RuntimeException(errorMessage, cause)
78

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.stt.connector.jira
2+
3+
class Issue (var summary: String? = null)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.stt.connector.jira
2+
3+
import com.jsoniter.JsonIterator
4+
import java.net.URI
5+
import java.net.http.HttpClient
6+
import java.net.http.HttpRequest
7+
import java.net.http.HttpResponse
8+
import java.util.Base64
9+
10+
class JiraClient(private var username: String?, private var password: String?, private var jiraUrl: String) {
11+
private val restApiV3 = "rest/api/3"
12+
private var restClient: HttpClient? = null
13+
14+
init {
15+
this.restClient = getJiraRestClient()
16+
}
17+
18+
private fun getJiraRestClient(): HttpClient? {
19+
return HttpClient.newBuilder().build()
20+
}
21+
22+
23+
fun getIssue(issueKey: String): Issue? {
24+
val jiraUrl = getJiraUrl()
25+
val request = HttpRequest.newBuilder()
26+
.uri(URI.create("$jiraUrl$restApiV3/issue/$issueKey?fields=summary"))
27+
.GET()
28+
.header("Accept", "application/json")
29+
.header("Content-Type", "application/json")
30+
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString(("$username:$password").toByteArray()))
31+
.build()
32+
this.restClient?.send(request, HttpResponse.BodyHandlers.ofString())?.let {
33+
if (it.statusCode() == 200) {
34+
val deserializedAsMap = JsonIterator.deserialize(it.body(), Map::class.java)
35+
val fields = deserializedAsMap["fields"] as Map<String, String>
36+
return Issue(fields["summary"])
37+
}
38+
handleHttpError(it, issueKey)
39+
}
40+
return null
41+
}
42+
43+
private fun handleHttpError(response: HttpResponse<String>, issueKey: String) {
44+
val httpStatusCode = response.statusCode()
45+
val body = response.body()
46+
when (httpStatusCode) {
47+
401 -> throw AccessDeniedException("You don't have permission to see $issueKey.")
48+
403 -> throw InvalidCredentialsException("Please check your Jira username/password.")
49+
404 -> throw IssueDoesNotExistException("Couldn't find issue $issueKey. Cause: $body")
50+
in 400..500 -> throw Exception("Error while retrieving issue $issueKey: $httpStatusCode: $body")
51+
}
52+
}
53+
54+
private fun getJiraUrl(): String {
55+
val jiraUrlAsCharArray = this.jiraUrl.toCharArray()
56+
if(jiraUrlAsCharArray[jiraUrlAsCharArray.size - 1] == '/') {
57+
return this.jiraUrl
58+
}
59+
return this.jiraUrl + "/"
60+
}
61+
62+
}
Lines changed: 21 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.stt.connector.jira
22

3-
//import net.rcarz.jiraclient.*
43
import org.stt.Service
54
import org.stt.config.JiraConfig
65
import java.nio.charset.StandardCharsets
@@ -11,25 +10,20 @@ import javax.inject.Singleton
1110
@Singleton
1211
class JiraConnector @Inject
1312
constructor(configuration: JiraConfig) : Service {
14-
// private val client: JiraClient?
15-
16-
// private val projectNames: Set<String> by lazy {
17-
// internalGetProjectNames()
18-
// }
13+
private val client: JiraClient?
1914

2015
init {
21-
// val jiraURI = configuration.jiraURI
22-
// client = jiraURI?.let {
23-
// if (configuration.jiraUsername != null
24-
// && configuration.jiraUsername!!.isNotEmpty()
25-
// && configuration.jiraPassword != null) {
26-
// JiraClient(jiraURI,
27-
// BasicCredentials(configuration.jiraUsername,
28-
// String(configuration.jiraPassword!!.password, StandardCharsets.UTF_8)))
29-
// } else {
30-
// JiraClient(jiraURI)
31-
// }
32-
// }
16+
val jiraURI = configuration.jiraURI
17+
client = jiraURI?.let {
18+
if (configuration.jiraUsername != null
19+
&& configuration.jiraUsername!!.isNotEmpty()
20+
&& configuration.jiraToken != null) {
21+
JiraClient(configuration.jiraUsername!!,
22+
String(configuration.jiraToken!!.password, StandardCharsets.UTF_8), jiraURI)
23+
} else {
24+
JiraClient(configuration.jiraUsername, null, jiraURI);
25+
}
26+
}
3327
}
3428

3529

@@ -41,61 +35,17 @@ constructor(configuration: JiraConfig) : Service {
4135
// no cleanup
4236
}
4337

44-
// fun getIssue(issueKey: String): Issue? {
45-
// if (client == null) {
46-
// return null
47-
// }
48-
//
49-
// val projectKey = getProjectKey(issueKey)
50-
//
51-
// // Check if the given project key belongs to an existing project
52-
// if (!projectExists(projectKey)) {
53-
// return null
54-
// }
55-
//
56-
// try {
57-
// return client.getIssue(issueKey)
58-
// } catch (e: JiraException) {
59-
// if (e.cause is RestException) {
60-
// val cause = e.cause as RestException
61-
// val httpStatusCode = cause.httpStatusCode
62-
// if (404 == httpStatusCode) {
63-
// throw IssueDoesNotExistException(String.format("Couldn't find issue %s.", issueKey), e)
64-
// } else if (401 == httpStatusCode) {
65-
// throw Exceptions(String.format("You don't have permission to see %s.", issueKey), e)
66-
// }
67-
// }
68-
// throw JiraConnectorException(String.format("Error while retrieving issue %s: %s", issueKey, e.cause?.localizedMessage), e)
69-
// }
38+
fun getIssue(issueKey: String): Issue? {
39+
if (client == null) {
40+
return null
41+
}
7042

71-
// }
43+
try {
44+
return client.getIssue(issueKey)
45+
} catch (e: Exception) {
46+
throw JiraConnectorException(String.format("Error while retrieving issue %s: %s", issueKey, e.cause?.localizedMessage), e)
47+
}
7248

73-
private fun projectExists(projectKey: String): Boolean {
74-
return false;//projectNames.contains(projectKey)
7549
}
7650

77-
private fun getProjectKey(issueKey: String): String {
78-
val index = issueKey.lastIndexOf('-')
79-
80-
// Extract the project key
81-
return if (index > 0) issueKey.substring(0, index) else issueKey
82-
}
83-
84-
// private fun internalGetProjectNames(): Set<String> {
85-
// try {
86-
// return client!!.projects
87-
// .map { it.key }
88-
// .toSet()
89-
// } catch (e: JiraException) {
90-
// if (e.cause is RestException) {
91-
// val cause = e.cause as RestException
92-
// val httpStatusCode = cause.httpStatusCode
93-
// if (httpStatusCode == 403 || httpStatusCode == 401) {
94-
// throw InvalidCredentialsException("Please check your Jira username/password.", e)
95-
// }
96-
// }
97-
// throw JiraConnectorException(String.format("Error retrieving projects from Jira: %s", e.localizedMessage), e)
98-
// }
99-
100-
// }
10151
}

src/main/kotlin/org/stt/text/JiraExpansionProvider.kt

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,21 @@ constructor(private val jiraConnector: JiraConnector,
1717
queryText = text.substring(spaceIndex, text.length).trim { it <= ' ' }
1818
}
1919

20-
return emptyList()
21-
22-
// return try {
23-
// jiraConnector.getIssue(queryText)?.summary?.let { listOf(": $it") } ?: emptyList()
24-
// } catch (e: JiraConnectorException) {
25-
// eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
26-
// emptyList()
27-
// } catch (e: Exceptions) {
28-
// eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
29-
// emptyList()
30-
// } catch (e: InvalidCredentialsException) {
31-
// eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
32-
// emptyList()
33-
// } catch (e: IssueDoesNotExistException) {
34-
// eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
35-
// emptyList()
36-
// }
20+
return try {
21+
jiraConnector.getIssue(queryText)?.summary?.let { listOf(": $it") } ?: emptyList()
22+
} catch (e: JiraConnectorException) {
23+
eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
24+
emptyList()
25+
} catch (e: Exceptions) {
26+
eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
27+
emptyList()
28+
} catch (e: InvalidCredentialsException) {
29+
eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
30+
emptyList()
31+
} catch (e: IssueDoesNotExistException) {
32+
eventBus.ifPresent { eb -> eb.publish(NotifyUser(e.message ?: "")) }
33+
emptyList()
34+
}
3735

3836
}
3937
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.stt.connector.jira
2+
3+
import org.junit.Test
4+
import org.stt.config.JiraConfig
5+
6+
internal class JiraClientTest {
7+
8+
@Test
9+
@Throws(AccessDeniedException::class, InvalidCredentialsException::class, IssueDoesNotExistException::class)
10+
fun testErrorHandlingOfIssueRequest() {
11+
val jiraConfig = JiraConfig()
12+
jiraConfig.jiraURI = "https://jira.atlassian.net"
13+
try {
14+
JiraClient("dummy", null, jiraConfig.jiraURI!!).getIssue("JRA-7")
15+
} catch (e: Exception) {
16+
assert(e.message.equals("Couldn't find issue JRA-7. Cause: {\"errorMessage\": \"Site temporarily unavailable\"}"))
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)