Skip to content

Commit 74cb4df

Browse files
authored
add expanded option to issue query parameters (#29)
1 parent 67666db commit 74cb4df

File tree

9 files changed

+183
-52
lines changed

9 files changed

+183
-52
lines changed

kotlin-jira-client/kotlin-jira-client-api/src/main/kotlin/com/linkedplanet/kotlinjiraclient/api/interfaces/JiraIssueOperator.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.google.gson.JsonObject
2424
import com.linkedplanet.kotlinatlassianclientcore.common.api.Page
2525
import com.linkedplanet.kotlinjiraclient.api.error.JiraClientError
2626
import com.linkedplanet.kotlinjiraclient.api.model.JiraIssue
27+
import com.linkedplanet.kotlinjiraclient.api.model.IssueQueryParams
2728

2829
/**
2930
* Provides methods for working with Jira issues, including retrieving issues by JQL query, issue type, or key; creating and updating issues; and deleting issues.
@@ -40,6 +41,7 @@ interface JiraIssueOperator<JiraFieldType> {
4041
*/
4142
suspend fun <T> getIssuesByJQL(
4243
jql: String,
44+
queryParams: IssueQueryParams = IssueQueryParams(),
4345
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
4446
): Either<JiraClientError, List<T>>
4547

@@ -55,6 +57,7 @@ interface JiraIssueOperator<JiraFieldType> {
5557
jql: String,
5658
pageIndex: Int = 0,
5759
pageSize: Int = RESULTS_PER_PAGE,
60+
queryParams: IssueQueryParams = IssueQueryParams(),
5861
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
5962
): Either<JiraClientError, Page<T>>
6063

@@ -66,6 +69,7 @@ interface JiraIssueOperator<JiraFieldType> {
6669
*/
6770
suspend fun <T> getIssueByJQL(
6871
jql: String,
72+
queryParams: IssueQueryParams = IssueQueryParams(),
6973
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
7074
): Either<JiraClientError, T?>
7175

@@ -79,6 +83,7 @@ interface JiraIssueOperator<JiraFieldType> {
7983
suspend fun <T> getIssuesByIssueType(
8084
projectId: Long,
8185
issueTypeId: Int,
86+
queryParams: IssueQueryParams = IssueQueryParams(),
8287
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
8388
): Either<JiraClientError, List<T>>
8489

@@ -96,6 +101,7 @@ interface JiraIssueOperator<JiraFieldType> {
96101
issueTypeId: Int,
97102
pageIndex: Int = 0,
98103
pageSize: Int = RESULTS_PER_PAGE,
104+
queryParams: IssueQueryParams = IssueQueryParams(),
99105
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
100106
): Either<JiraClientError, Page<T>>
101107

@@ -108,6 +114,7 @@ interface JiraIssueOperator<JiraFieldType> {
108114
*/
109115
suspend fun <T> getIssueByKey(
110116
key: String,
117+
queryParams: IssueQueryParams = IssueQueryParams(),
111118
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
112119
): Either<JiraClientError, T?>
113120

@@ -119,6 +126,7 @@ interface JiraIssueOperator<JiraFieldType> {
119126
*/
120127
suspend fun <T> getIssueById(
121128
id: Int,
129+
queryParams: IssueQueryParams = IssueQueryParams(),
122130
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
123131
): Either<JiraClientError, T?>
124132

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*-
2+
* #%L
3+
* kotlin-jira-client-api
4+
* %%
5+
* Copyright (C) 2022 - 2025 linked-planet GmbH
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.linkedplanet.kotlinjiraclient.api.model
21+
22+
enum class ExpandedOption(val key: String) {
23+
VERSIONED_REPRESENTATIONS("versionedRepresentations"),
24+
RENDERED_FIELDS("renderedFields"),
25+
NAMES("names"),
26+
SCHEMA("schema"),
27+
TRANSITIONS("transitions"),
28+
OPERATIONS("operations"),
29+
EDITMETA("editmeta"),
30+
CHANGELOG("changelog");
31+
32+
companion object {
33+
fun fromKey(key: String): ExpandedOption? {
34+
return values().find { it.key == key }
35+
}
36+
}
37+
38+
override fun toString(): String {
39+
return this.key
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*-
2+
* #%L
3+
* kotlin-jira-client-api
4+
* %%
5+
* Copyright (C) 2022 - 2025 linked-planet GmbH
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.linkedplanet.kotlinjiraclient.api.model
21+
22+
import com.linkedplanet.kotlinjiraclient.api.model.ExpandedOption.NAMES
23+
import com.linkedplanet.kotlinjiraclient.api.model.ExpandedOption.TRANSITIONS
24+
25+
/**
26+
* Controls expansion and other parameters of the returned Jira Issue.
27+
*
28+
* The following parameters are planed for the future: fields, fieldsByKeys, properties
29+
*/
30+
data class IssueQueryParams(
31+
val expanded: List<ExpandedOption> = listOf(NAMES, TRANSITIONS)
32+
)

kotlin-jira-client/kotlin-jira-client-http/src/main/kotlin/com/linkedplanet/kotlinjiraclient/http/HttpJiraIssueOperator.kt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.linkedplanet.kotlinjiraclient.api.error.JiraClientError
2828
import com.linkedplanet.kotlinjiraclient.api.interfaces.JiraIssueOperator
2929
import com.linkedplanet.kotlinjiraclient.api.model.JiraIssue
3030
import com.linkedplanet.kotlinatlassianclientcore.common.api.Page
31+
import com.linkedplanet.kotlinjiraclient.api.model.IssueQueryParams
3132
import com.linkedplanet.kotlinjiraclient.http.field.HttpJiraField
3233
import com.linkedplanet.kotlinjiraclient.http.model.HttpMappingField
3334
import com.linkedplanet.kotlinjiraclient.http.util.fromHttpDomainError
@@ -41,6 +42,7 @@ class HttpJiraIssueOperator(private val context: HttpJiraClientContext) : JiraIs
4142

4243
override suspend fun <T> getIssuesByJQL(
4344
jql: String,
45+
queryParams: IssueQueryParams,
4446
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
4547
): Either<JiraClientError, List<T>> = either {
4648
recursiveRestCallPaginatedRaw { index, maxSize ->
@@ -51,7 +53,7 @@ class HttpJiraIssueOperator(private val context: HttpJiraClientContext) : JiraIs
5153
"jql" to jql,
5254
"startAt" to index.toString(),
5355
"maxResults" to maxSize.toString(),
54-
"expand" to "names,transitions",
56+
"expand" to queryParams.expanded.joinToString(","),
5557
),
5658
null,
5759
"application/json",
@@ -75,6 +77,7 @@ class HttpJiraIssueOperator(private val context: HttpJiraClientContext) : JiraIs
7577
jql: String,
7678
pageIndex: Int,
7779
pageSize: Int,
80+
queryParams: IssueQueryParams,
7881
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
7982
): Either<JiraClientError, Page<T>> = either {
8083
val page = context.httpClient.executeGet<HttpJiraIssuePage>(
@@ -83,7 +86,7 @@ class HttpJiraIssueOperator(private val context: HttpJiraClientContext) : JiraIs
8386
"jql" to jql,
8487
"startAt" to (pageIndex * pageSize).toString(),
8588
"maxResults" to pageSize.toString(),
86-
"expand" to "names,transitions",
89+
"expand" to queryParams.expanded.joinToString(","),
8790
),
8891
object : TypeToken<HttpJiraIssuePage>() {}.type
8992
)
@@ -111,37 +114,43 @@ class HttpJiraIssueOperator(private val context: HttpJiraClientContext) : JiraIs
111114

112115
override suspend fun <T> getIssueByJQL(
113116
jql: String,
117+
queryParams: IssueQueryParams,
114118
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
115119
): Either<JiraClientError, T?> = either {
116-
getIssuesByJQLPaginated(jql, 0, 1, parser).bind().items.firstOrNull()
120+
getIssuesByJQLPaginated(jql, 0, 1, queryParams, parser).bind().items.firstOrNull()
117121
}
118122

119123
override suspend fun <T> getIssuesByIssueType(
120124
projectId: Long,
121125
issueTypeId: Int,
126+
queryParams: IssueQueryParams,
122127
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
123128
): Either<JiraClientError, List<T>> = either {
124-
getIssuesByJQL("project=$projectId AND issueType=$issueTypeId", parser).bind()
129+
val jql = "project=$projectId AND issueType=$issueTypeId"
130+
getIssuesByJQL(jql, queryParams, parser).bind()
125131
}
126132

127133
override suspend fun <T> getIssuesByTypePaginated(
128134
projectId: Long,
129135
issueTypeId: Int,
130136
pageIndex: Int,
131137
pageSize: Int,
138+
queryParams: IssueQueryParams,
132139
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
133140
): Either<JiraClientError, Page<T>> = either {
134-
getIssuesByJQLPaginated("project=$projectId AND issueType=$issueTypeId", pageIndex, pageSize, parser).bind()
141+
val jql = "project=$projectId AND issueType=$issueTypeId"
142+
getIssuesByJQLPaginated(jql, pageIndex, pageSize, queryParams, parser).bind()
135143
}
136144

137145
override suspend fun <T> getIssueByKey(
138146
key: String,
147+
queryParams: IssueQueryParams,
139148
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
140149
): Either<JiraClientError, T?> = either {
141150
val successResponse = context.httpClient.executeGetCall(
142151
"/rest/api/2/issue/${key}",
143152
mapOf(
144-
"expand" to "names,transitions"
153+
"expand" to queryParams.expanded.joinToString(","),
145154
),
146155
)
147156
.map { it.body }
@@ -167,8 +176,9 @@ class HttpJiraIssueOperator(private val context: HttpJiraClientContext) : JiraIs
167176

168177
override suspend fun <T> getIssueById(
169178
id: Int,
179+
queryParams: IssueQueryParams,
170180
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
171-
): Either<JiraClientError, T?> = getIssueByKey(id.toString(), parser)
181+
): Either<JiraClientError, T?> = getIssueByKey(id.toString(), queryParams, parser)
172182

173183
override suspend fun createIssue(
174184
projectId: Long,

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

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import com.linkedplanet.kotlinatlassianclientcore.common.error.asEither
4545
import com.linkedplanet.kotlinjiraclient.api.error.JiraClientError
4646
import com.linkedplanet.kotlinjiraclient.api.interfaces.JiraIssueOperator
4747
import com.linkedplanet.kotlinjiraclient.api.model.JiraIssue
48+
import com.linkedplanet.kotlinjiraclient.api.model.IssueQueryParams
4849
import com.linkedplanet.kotlinjiraclient.sdk.field.SdkJiraField
4950
import com.linkedplanet.kotlinjiraclient.sdk.util.IssueJsonConverter
5051
import com.linkedplanet.kotlinjiraclient.sdk.util.catchJiraClientError
@@ -134,15 +135,17 @@ object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
134135

135136
override suspend fun <T> getIssueById(
136137
id: Int,
138+
queryParams: IssueQueryParams,
137139
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
138140
): Either<JiraClientError, T?> =
139-
getIssueByKey(id.toString(), parser)
141+
getIssueByKey(id.toString(), queryParams, parser)
140142

141143
override suspend fun <T> getIssueByJQL(
142144
jql: String,
145+
queryParams: IssueQueryParams,
143146
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
144147
): Either<JiraClientError, T?> = either {
145-
val potentiallyMultipleIssues = getIssuesByJQLPaginated(jql, 0, 1, parser).bind()
148+
val potentiallyMultipleIssues = getIssuesByJQLPaginated(jql, 0, 1, queryParams, parser).bind()
146149
if (potentiallyMultipleIssues.totalItems < 1) {
147150
JiraClientError("Issue not found", "No issue was found.").asEither<JiraClientError, T?>().bind()
148151
}
@@ -152,21 +155,28 @@ object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
152155
override suspend fun <T> getIssuesByIssueType(
153156
projectId: Long,
154157
issueTypeId: Int,
158+
queryParams: IssueQueryParams,
155159
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
156-
): Either<JiraClientError, List<T>> =
157-
getIssuesByJQL("project=$projectId AND issueType=$issueTypeId", parser)
160+
): Either<JiraClientError, List<T>> {
161+
val jql = "project=$projectId AND issueType=$issueTypeId"
162+
return getIssuesByJQL(jql, queryParams, parser)
163+
}
158164

159165
override suspend fun <T> getIssuesByTypePaginated(
160166
projectId: Long,
161167
issueTypeId: Int,
162168
pageIndex: Int,
163169
pageSize: Int,
170+
queryParams: IssueQueryParams,
164171
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
165-
): Either<JiraClientError, Page<T>> =
166-
getIssuesByJQLPaginated("project=$projectId AND issueType=$issueTypeId", pageIndex, pageSize, parser)
172+
): Either<JiraClientError, Page<T>> {
173+
val jql = "project=$projectId AND issueType=$issueTypeId"
174+
return getIssuesByJQLPaginated(jql, pageIndex, pageSize, queryParams, parser)
175+
}
167176

168177
override suspend fun <T> getIssueByKey(
169178
key: String,
179+
queryParams: IssueQueryParams,
170180
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
171181
): Either<JiraClientError, T?> = either {
172182
Either.catchJiraClientError {
@@ -176,45 +186,51 @@ object SdkJiraIssueOperator : JiraIssueOperator<SdkJiraField> {
176186
}
177187
val issue = issueResult.toEither().bind().issue
178188
?: return@catchJiraClientError null
179-
issueToConcreteType(issue, parser).bind()
189+
issueToConcreteType(issue, queryParams, parser).bind()
180190
}.bind()
181191
}
182192

183193
override suspend fun <T> getIssuesByJQL(
184194
jql: String,
195+
queryParams: IssueQueryParams,
185196
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
186197
): Either<JiraClientError, List<T>> = either {
187-
val issuePage = getIssuesByJqlWithPagerFilter(jql, PagerFilter.getUnlimitedFilter(), parser).bind()
198+
val issuePage = getIssuesByJqlWithPagerFilter(jql, PagerFilter.getUnlimitedFilter(), queryParams, parser).bind()
188199
issuePage.items
189200
}
190201

191202
override suspend fun <T> getIssuesByJQLPaginated(
192203
jql: String,
193204
pageIndex: Int,
194205
pageSize: Int,
206+
queryParams: IssueQueryParams,
195207
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
196-
): Either<JiraClientError, Page<T>> =
197-
getIssuesByJqlWithPagerFilter(jql, PagerFilter.newPageAlignedFilter(pageIndex * pageSize, pageSize), parser)
208+
): Either<JiraClientError, Page<T>> {
209+
val pagerFilter = PagerFilter.newPageAlignedFilter(pageIndex * pageSize, pageSize)
210+
return getIssuesByJqlWithPagerFilter(jql, pagerFilter, queryParams, parser)
211+
}
198212

199213
private suspend fun <T> issueToConcreteType(
200214
issue: Issue,
215+
queryParams: IssueQueryParams,
201216
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
202217
): Either<JiraClientError, T> = Either.catchJiraClientError {
203-
val jsonIssue: JsonObject = issueJsonConverter.createJsonIssue(issue)
218+
val jsonIssue: JsonObject = issueJsonConverter.createJsonIssue(issue, queryParams)
204219
val customFieldMap = customFieldManager.getCustomFieldObjects(issue).associate { it.name to it.id }
205220
return parser(jsonIssue, customFieldMap)
206221
}
207222

208223
private suspend fun <T> getIssuesByJqlWithPagerFilter(
209224
jql: String,
210225
pagerFilter: PagerFilter<*>?,
226+
queryParams: IssueQueryParams,
211227
parser: suspend (JsonObject, Map<String, String>) -> Either<JiraClientError, T>
212228
): Either<JiraClientError, Page<T>> = either {
213229
val user = userOrError().bind()
214230
val query = Either.catchJiraClientError { jqlParser.parseQuery(jql) }.bind()
215231
val search = Either.catchJiraClientError { searchService.search(user, query, pagerFilter) }.bind()
216232
val issues = search.results
217-
.map { issue -> issueToConcreteType(issue, parser) }
233+
.map { issue -> issueToConcreteType(issue, queryParams, parser) }
218234
.bindAll()
219235
val totalItems = search.total
220236
val pageSize = pagerFilter?.pageSize ?: 0

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import com.atlassian.jira.rest.v2.issue.IncludedFields
3030
import com.atlassian.jira.rest.v2.issue.IssueBean
3131
import com.atlassian.jira.rest.v2.issue.builder.BeanBuilderFactory
3232
import com.google.gson.*
33+
import com.linkedplanet.kotlinjiraclient.api.model.IssueQueryParams
3334
import com.linkedplanet.kotlinjiraclient.sdk.field.FieldAccessorImpl
3435
import org.slf4j.LoggerFactory
3536
import javax.ws.rs.core.UriBuilder
@@ -66,10 +67,13 @@ class IssueJsonConverter {
6667

6768

6869
@Throws(FieldException::class)
69-
fun createJsonIssue(issue: Issue): JsonObject {
70-
val expand = "names,transitions"
70+
fun createJsonIssue(
71+
issue: Issue,
72+
queryParams: IssueQueryParams,
73+
): JsonObject {
74+
val expanded = queryParams.expanded.joinToString(",")
7175
val issueBean: IssueBean = beanBuilderFactory
72-
.newIssueBeanBuilder2(IncludedFields.includeNavigableByDefault(null), expand, uriBuilder)
76+
.newIssueBeanBuilder2(IncludedFields.includeNavigableByDefault(null), expanded, uriBuilder)
7377
.build(issue)
7478
this.addOrderableFieldsToBean(issueBean, issue)
7579
this.addAvailableNavigableFieldsToBean(issueBean, issue)

0 commit comments

Comments
 (0)