Skip to content

Commit 092d822

Browse files
committed
chore: simplify pagination iterator state and query-param parsing
Three behavior-preserving cleanups in the pagination package: - Paginator: seed the iterator's nextRequest with initialRequest so the first fetch reads it like every later page, removing the started flag and the dead `nextRequest ?: nextPageRequest()` fallback. The page-budget and cursor mutations stay after the fetch, so the catch-and-retry-hasNext path still re-attempts the same request on a failed exchange. - RequestRebuilder.getQueryParam: parse each segment with substringBefore/ substringAfter on the first '=', matching setQueryParam's existing idiom and dropping the manual indexOf/substring bookkeeping. Edge cases (no '=', empty value, '=' in value) are identical. - LinkHeaderPaginationStrategy: drop the empty-string guards around the Link header join. An empty values list joins to "" and extractNextUrl("") already returns null, so the guards were dead. No public-API change; existing pagination tests stay green.
1 parent c1da824 commit 092d822

3 files changed

Lines changed: 17 additions & 39 deletions

File tree

sdk-core/src/main/kotlin/org/dexpace/sdk/core/pagination/LinkHeaderPaginationStrategy.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,10 @@ public class LinkHeaderPaginationStrategy<T>
5555
): Page<T> {
5656
val items: List<T> = itemsExtractor(response)
5757
// Some servers emit multiple Link headers (one per link-value) instead of a single
58-
// comma-separated header. Join with ',' so a single parser handles both shapes.
59-
val linkValues: List<String> = response.headers.values(linkHeader)
60-
val combined: String =
61-
if (linkValues.isEmpty()) "" else linkValues.joinToString(separator = ",")
62-
val nextUrlString: String? = if (combined.isEmpty()) null else extractNextUrl(combined)
58+
// comma-separated header. Join with ',' so a single parser handles both shapes; an
59+
// empty values list joins to "", and extractNextUrl("") already returns null.
60+
val nextUrlString: String? =
61+
extractNextUrl(response.headers.values(linkHeader).joinToString(separator = ","))
6362
// Resolve the (possibly relative) next-link target against the originating page's
6463
// URL. A target that cannot be parsed into a valid URL ends the stream instead of
6564
// aborting the whole iteration with a MalformedURLException.

sdk-core/src/main/kotlin/org/dexpace/sdk/core/pagination/Paginator.kt

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,10 @@ public class Paginator<T>
145145
// Index into currentPage.items for the next item to emit.
146146
private var currentItemIndex: Int = 0
147147

148-
// null = first iteration (use initialRequest); else the next-page request scheduled by
149-
// the previous page's strategy.parse() call.
150-
private var nextRequest: Request? = null
151-
152-
// true once we have fetched at least one page. Used to distinguish "never fetched" from
153-
// "exhausted after fetching" — both have currentPage == null right after iteration ends.
154-
private var started: Boolean = false
148+
// The next request to fetch: seeded with initialRequest, then replaced after each page
149+
// with that page's nextPageRequest(), or null once a page reports no successor (which
150+
// ends the stream on the following advance()).
151+
private var nextRequest: Request? = initialRequest
155152

156153
// true after iteration is definitively over; prevents further fetches.
157154
private var done: Boolean = false
@@ -190,26 +187,11 @@ public class Paginator<T>
190187
* [currentPage]; `false` if iteration is now done.
191188
*/
192189
private fun advance(): Boolean {
193-
if (pagesFetched >= maxPages) {
194-
// Safety cap reached: stop before fetching a page we would otherwise yield,
195-
// even if the previous page still reports hasNext.
196-
done = true
197-
currentPage = null
198-
return false
199-
}
200-
val request: Request? =
201-
if (!started) {
202-
initialRequest
203-
} else {
204-
// We have a current page that's exhausted — see if it can produce a next.
205-
val finishedPage = currentPage
206-
if (finishedPage == null || !finishedPage.hasNext) {
207-
null
208-
} else {
209-
nextRequest ?: finishedPage.nextPageRequest()
210-
}
211-
}
212-
if (request == null) {
190+
val request = nextRequest
191+
if (request == null || pagesFetched >= maxPages) {
192+
// Either no next request is scheduled (the previous page had no successor) or
193+
// the safety cap is reached — stop before fetching a page we would otherwise
194+
// yield, even if the previous page still reports hasNext.
213195
done = true
214196
currentPage = null
215197
return false
@@ -222,11 +204,10 @@ public class Paginator<T>
222204
} finally {
223205
response.close()
224206
}
225-
started = true
226207
currentPage = page
227208
currentItemIndex = 0
228209
// Compute the next request now so we don't have to retain the (closed) response.
229-
// If this page has no next, nextRequest stays null; advance() will treat that as done.
210+
// If this page has no next, nextRequest goes null; the next advance() ends the stream.
230211
nextRequest = if (page.hasNext) page.nextPageRequest() else null
231212
return true
232213
}

sdk-core/src/main/kotlin/org/dexpace/sdk/core/pagination/RequestRebuilder.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,9 @@ internal object RequestRebuilder {
114114
if (existing.isEmpty()) return null
115115
for (segment in existing.split('&')) {
116116
if (segment.isEmpty()) continue
117-
val eq = segment.indexOf('=')
118-
val rawKey = if (eq >= 0) segment.substring(0, eq) else segment
119-
if (decodeOrRaw(rawKey) == name) {
120-
val rawValue = if (eq >= 0) segment.substring(eq + 1) else ""
121-
return decodeOrRaw(rawValue)
117+
val key = segment.substringBefore('=', segment)
118+
if (decodeOrRaw(key) == name) {
119+
return decodeOrRaw(segment.substringAfter('=', ""))
122120
}
123121
}
124122
return null

0 commit comments

Comments
 (0)