Skip to content

Commit cf44aae

Browse files
authored
Merge pull request #19 from linked-planet/feature/permission_tests
Feature/permission tests
2 parents d37315d + 563abc8 commit cf44aae

File tree

10 files changed

+338
-128
lines changed

10 files changed

+338
-128
lines changed

kotlin-atlassian-client-core-common/src/main/kotlin/com/linkedplanet/kotlinatlassianclientcore/common/error/AtlassianClientError.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,29 @@ open class AtlassianClientError(
2929
@field:NotNull val message: String,
3030
@field:NotNull val stacktrace: String = ""
3131
) {
32+
33+
override fun equals(other: Any?): Boolean {
34+
if (this === other) return true
35+
if (other !is AtlassianClientError) return false
36+
37+
if (error != other.error) return false
38+
if (message != other.message) return false
39+
if (stacktrace != other.stacktrace) return false
40+
41+
return true
42+
}
43+
44+
override fun hashCode(): Int {
45+
var result = error.hashCode()
46+
result = 31 * result + message.hashCode()
47+
result = 31 * result + stacktrace.hashCode()
48+
return result
49+
}
50+
51+
override fun toString(): String {
52+
return "AtlassianClientError(error='$error', message='$message', stacktrace='$stacktrace')"
53+
}
54+
3255
companion object
3356
}
3457

kotlin-jira-client/kotlin-jira-client-sdk/src/main/kotlin/com/linkedplanet/kotlinjiraclient/sdk/SdkJiraIssueOperator.kt

Lines changed: 95 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,30 @@
1919
*/
2020
package com.linkedplanet.kotlinjiraclient.sdk
2121

22-
import arrow.core.*
22+
import arrow.core.Either
23+
import arrow.core.left
2324
import arrow.core.raise.either
25+
import arrow.core.right
26+
import com.atlassian.jira.bc.ServiceResult
2427
import com.atlassian.jira.bc.issue.search.SearchService
2528
import com.atlassian.jira.component.ComponentAccessor
2629
import com.atlassian.jira.event.type.EventDispatchOption
2730
import com.atlassian.jira.issue.Issue
31+
import com.atlassian.jira.issue.IssueInputParameters
2832
import com.atlassian.jira.issue.MutableIssue
2933
import com.atlassian.jira.jql.parser.JqlQueryParser
34+
import com.atlassian.jira.user.ApplicationUser
35+
import com.atlassian.jira.util.ErrorCollection
36+
import com.atlassian.jira.util.ErrorCollection.Reason
37+
import com.atlassian.jira.util.ErrorCollections
38+
import com.atlassian.jira.web.bean.I18nBean
3039
import com.atlassian.jira.web.bean.PagerFilter
3140
import com.google.gson.JsonObject
41+
import com.linkedplanet.kotlinatlassianclientcore.common.api.Page
42+
import com.linkedplanet.kotlinatlassianclientcore.common.error.asEither
3243
import com.linkedplanet.kotlinjiraclient.api.error.JiraClientError
3344
import com.linkedplanet.kotlinjiraclient.api.interfaces.JiraIssueOperator
3445
import com.linkedplanet.kotlinjiraclient.api.model.JiraIssue
35-
import com.linkedplanet.kotlinatlassianclientcore.common.api.Page
36-
import com.linkedplanet.kotlinatlassianclientcore.common.error.asEither
3746
import com.linkedplanet.kotlinjiraclient.sdk.field.SdkJiraField
3847
import com.linkedplanet.kotlinjiraclient.sdk.util.IssueJsonConverter
3948
import com.linkedplanet.kotlinjiraclient.sdk.util.catchJiraClientError
@@ -44,8 +53,7 @@ import kotlin.math.ceil
4453
object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
4554
override var RESULTS_PER_PAGE: Int = 10
4655

47-
private val issueManager by lazy { ComponentAccessor.getIssueManager() }
48-
private val issueFactory by lazy { ComponentAccessor.getIssueFactory() }
56+
private val issueService by lazy { ComponentAccessor.getIssueService() }
4957
private val customFieldManager by lazy { ComponentAccessor.getCustomFieldManager() }
5058
private val searchService: SearchService by lazy { ComponentAccessor.getComponent(SearchService::class.java) }
5159
private val jiraAuthenticationContext by lazy { ComponentAccessor.getJiraAuthenticationContext() }
@@ -60,43 +68,79 @@ object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
6068
projectId: Long,
6169
issueTypeId: Int,
6270
fields: List<SdkJiraField>
63-
): Either<JiraClientError, JiraIssue?> = Either.catchJiraClientError {
64-
val freshIssue: MutableIssue = issueFactory.issue
65-
freshIssue.projectId = projectId
66-
freshIssue.issueTypeId = issueTypeId.toString()
67-
fields.forEach { field ->
68-
field.render(freshIssue)
69-
}
70-
val createdIssue: Issue = issueManager.createIssueObject(user(), freshIssue)
71+
): Either<JiraClientError, JiraIssue?> = either {
72+
Either.catchJiraClientError {
73+
val inputParameters = issueInputParameters(projectId, issueTypeId, fields)
74+
val validateCreate = issueService.validateCreate(user(), inputParameters).toEither().bind()
75+
val createResult = issueService.create(user(), validateCreate).toEither().bind()
76+
toBasicReturnTypeIssue(createResult.issue)
77+
}.bind()
78+
}
79+
80+
private fun toBasicReturnTypeIssue(createdIssue: MutableIssue): JiraIssue {
7181
val basePath = applicationProperties.jiraBaseUrl
7282
val contextPath = webResourceUrlProvider.baseUrl
7383
val fullPath = if (contextPath.isNotEmpty()) "$basePath/$contextPath" else basePath
7484
val selfLink = fullPath + "/rest/api/2/issue/" + createdIssue.id
75-
JiraIssue(createdIssue.id.toString(), createdIssue.key, selfLink)
85+
return JiraIssue(createdIssue.id.toString(), createdIssue.key, selfLink)
7686
}
7787

7888
override suspend fun updateIssue(
7989
projectId: Long,
8090
issueTypeId: Int,
8191
issueKey: String,
8292
fields: List<SdkJiraField>
83-
): Either<JiraClientError, Unit> = Either.catchJiraClientError {
84-
val issue = issueManager.getIssueByCurrentKey(issueKey)
85-
issue.projectId = projectId
86-
issue.issueTypeId = issueTypeId.toString()
93+
): Either<JiraClientError, Unit> = either {
94+
Either.catchJiraClientError {
95+
val issueId = issueService.getIssue(user(), issueKey).toEither().bind().issue.id
96+
val inputParameters = issueInputParameters(projectId, issueTypeId, fields)
97+
val validationResult = issueService.validateUpdate(user(), issueId, inputParameters).toEither().bind()
98+
issueService.update(user(), validationResult, EventDispatchOption.ISSUE_UPDATED, false).toEither().bind()
99+
}.bind()
100+
}
101+
102+
private fun issueInputParameters(
103+
projectId: Long,
104+
issueTypeId: Int,
105+
fields: List<SdkJiraField>
106+
): IssueInputParameters? {
107+
val issueInput = issueService.newIssueInputParameters()
108+
issueInput.projectId = projectId
109+
issueInput.issueTypeId = issueTypeId.toString()
87110
fields.forEach { field ->
88-
field.render(issue)
111+
field.render(issueInput)
89112
}
90-
issueManager.updateIssue(user(), issue, EventDispatchOption.ISSUE_UPDATED, false)
113+
return issueInput
91114
}
92115

93-
override suspend fun deleteIssue(issueKey: String): Either<JiraClientError, Unit> =
116+
override suspend fun deleteIssue(issueKey: String): Either<JiraClientError, Unit> = either {
94117
Either.catchJiraClientError {
95-
val issue: Issue = issueManager.getIssueByCurrentKey(issueKey)
96-
val sendMail = false
97-
issueManager.deleteIssue(user(), issue, EventDispatchOption.ISSUE_DELETED, sendMail)
118+
val issueToDelete = issueService.getIssue(user(), issueKey).toEither().bind()
119+
val validateDelete = issueService.validateDelete(user(), issueToDelete.issue.id).toEither().bind()
120+
issueService.delete(user(), validateDelete, EventDispatchOption.ISSUE_DELETED, false).toEither().bind()
121+
}.bind()
122+
}
123+
124+
private fun <T : ServiceResult> T.toEither() : Either<JiraClientError, T> =
125+
when {
126+
this.isValid -> Either.Right(this)
127+
else -> Either.Left(jiraClientError(this.errorCollection))
98128
}
99129

130+
private fun ErrorCollection.toEither() : Either<JiraClientError, Unit> =
131+
when {
132+
this.hasAnyErrors() -> jiraClientError(this).left()
133+
else -> Unit.right()
134+
}
135+
136+
private fun jiraClientError(errorCollection: ErrorCollection): JiraClientError {
137+
val worstReason = Reason.getWorstReason(errorCollection.reasons)
138+
return JiraClientError(
139+
"DeleteFailed",
140+
errorCollection.errorMessages.joinToString() + " (${worstReason.httpStatusCode})"
141+
)
142+
}
143+
100144
override suspend fun <T> getIssueById(
101145
id: Int,
102146
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
@@ -133,11 +177,16 @@ object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
133177
override suspend fun <T> getIssueByKey(
134178
key: String,
135179
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
136-
): Either<JiraClientError, T?> = Either.catchJiraClientError {
137-
val issue = issueManager.getIssueByCurrentKey(key)
138-
?: return@catchJiraClientError null
139-
140-
return issueToConcreteType(issue, parser)
180+
): Either<JiraClientError, T?> = either {
181+
Either.catchJiraClientError {
182+
val issueResult = issueService.getIssue(user(), key)
183+
if (Reason.getWorstReason(issueResult.errorCollection.reasons) == Reason.NOT_FOUND){
184+
return@catchJiraClientError null
185+
}
186+
val issue = issueResult.toEither().bind().issue
187+
?: return@catchJiraClientError null
188+
issueToConcreteType(issue, parser).bind()
189+
}.bind()
141190
}
142191

143192
override suspend fun <T> getIssuesByJQL(
@@ -170,16 +219,30 @@ object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
170219
pagerFilter: PagerFilter<*>?,
171220
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
172221
): Either<JiraClientError, Page<T>> = either {
222+
val user = userOrError().bind()
173223
val query = jqlParser.parseQuery(jql)
174-
val search = searchService.search(user(), query, pagerFilter)
224+
val search = searchService.search(user, query, pagerFilter)
175225
val issues = search.results
176226
.map { issue -> issueToConcreteType(issue, parser) }
177-
.sequenceEither()
178-
.bind()
227+
.bindAll()
179228
val totalItems = search.total
180229
val pageSize = pagerFilter?.pageSize ?: 0
181230
val totalPages = ceil(totalItems.toDouble() / pageSize.toDouble()).toInt()
182231
val currentPageIndex = pagerFilter?.start?.let { start -> start / pageSize } ?: 0
183232
Page(issues, totalItems, totalPages, currentPageIndex, pageSize)
184233
}
234+
235+
private fun userOrError() : Either<JiraClientError, ApplicationUser> = either {
236+
val applicationUser = user()
237+
return applicationUser?.right()
238+
?: jiraClientError(
239+
ErrorCollections
240+
.create(
241+
I18nBean(I18nBean.getLocaleFromUser(applicationUser))
242+
.getText("admin.errors.issues.no.permission.to.see"),
243+
Reason.NOT_LOGGED_IN
244+
)
245+
).left()
246+
}
247+
185248
}

0 commit comments

Comments
 (0)