Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DSL overloads to paginator methods #591

Merged
merged 2 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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 @@ -379,9 +379,23 @@ private fun String.stripAll(stripList: List<String>): String {
}

// Remove whitespace from the beginning and end of each line of documentation
// Remove blank lines
// Remove leading, trailing, and consecutive blank lines
private fun formatDocumentation(doc: String, lineSeparator: String = "\n") =
doc
.split('\n') // Break the doc into lines
.filter { it.isNotBlank() } // Remove empty lines
.dropWhile { it.isBlank() } // Drop leading blank lines
.dropLastWhile { it.isBlank() } // Drop trailing blank lines
.filter(NoConsecutivesFilter { it.isBlank() }) // Remove consecutive empty lines
.joinToString(separator = lineSeparator) { it.trim() } // Trim line

/**
* A filtering predicate for removing consecutive lines matching some condition. When passed to a `filter` method, it
* will remove any item matching the [predicate] if the previous item also matched the [predicate]. (Single instances of
* items matching the predicate will not be removed.)
*/
private class NoConsecutivesFilter<T>(private val predicate: (T) -> Boolean): (T) -> Boolean {
var lastItemMatched = false

override operator fun invoke(item: T): Boolean =
!lastItemMatched or !predicate(item).also { lastItemMatched = it }
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,36 +108,35 @@ class PaginatorGenerator : KotlinIntegration {
}
val markerLiteral = paginationInfo.inputTokenMember.defaultName()

val docBody = """
Paginate over [${outputSymbol.name}] results.

When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service
calls are made until the flow is collected. This also means there is no guarantee that the request is valid
until then. Once you start collecting the flow, the SDK will lazily load response pages by making service
calls until there are no pages left or the flow is cancelled. If there are errors in your request, you will
see the failures only after you start collection.
""".trimIndent()
val docReturn = "@return A [kotlinx.coroutines.flow.Flow] that can collect [${outputSymbol.name}]"

writer.write("")
writer.dokka(
"""
Paginate over [${outputSymbol.name}] results.
When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service calls are
made until the flow is collected. This also means there is no guarantee that the request is valid until then. Once
you start collecting the flow, the SDK will lazily load response pages by making service calls until there are no
pages left or the flow is cancelled. If there are errors in your request, you will see the failures only after you start
collection.
@param initialRequest A [${inputSymbol.name}] to start pagination
@return A [kotlinx.coroutines.flow.Flow] that can collect [${outputSymbol.name}]
""".trimIndent()
)
writer
.addImport(ExternalTypes.KotlinxCoroutines.Flow)
.addImport(ExternalTypes.KotlinxCoroutines.FlowGenerator)
.addImport(serviceSymbol)
.addImport(inputSymbol)
.addImport(outputSymbol)
.addImport(cursorSymbol)
.dokka("""
$docBody
@param initialRequest A [${inputSymbol.name}] to start pagination
$docReturn
""".trimIndent())
.addImportReferences(cursorSymbol, SymbolReference.ContextOption.DECLARE)
.withBlock(
"fun #T.#LPaginated(initialRequest: #T): Flow<#T> =",
"fun #T.#LPaginated(initialRequest: #T): #T<#T> =",
"",
serviceSymbol,
operationShape.defaultName(),
inputSymbol,
outputSymbol
ExternalTypes.KotlinxCoroutines.Flow,
outputSymbol,
) {
withBlock("flow {", "}") {
withBlock("#T {", "}", ExternalTypes.KotlinxCoroutines.FlowGenerator) {
write("var cursor: #F = null", cursorSymbol)
write("var isFirstPage: Boolean = true")
write("")
Expand All @@ -155,6 +154,25 @@ class PaginatorGenerator : KotlinIntegration {
}
}
}

writer.write("")
writer
.dokka("""
$docBody
@param block A builder block used for DSL-style invocation of the operation
$docReturn
""".trimIndent())
.withBlock(
"fun #T.#LPaginated(block: #T.Builder.() -> Unit): #T<#T> =",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KotlinTypes.Unit is available as well and we may want that to be fully qualified since there is a model with Unit as a shape IIRC

"",
serviceSymbol,
operationShape.defaultName(),
inputSymbol,
ExternalTypes.KotlinxCoroutines.Flow,
outputSymbol,
) {
write("#LPaginated(#T.Builder().apply(block).build())", operationShape.defaultName(), inputSymbol)
}
}

// Generate a paginator that iterates over the model-specified item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ class Config private constructor(builder: Builder): HttpClientConfig, Idempotenc
/**
* Configure events that will be logged. By default clients will not output
* raw requests or responses. Use this setting to opt-in to additional debug logging.
*
* This can be used to configure logging of requests, responses, retries, etc of SDK clients.
*
* **NOTE**: Logging of raw requests or responses may leak sensitive information! It may also have
* performance considerations when dumping the request/response body. This is primarily a tool for
* debug purposes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,12 @@ class PaginatorGeneratorTest {
val expected = """
/**
* Paginate over [ListFunctionsResponse] results.
* When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service calls are
* made until the flow is collected. This also means there is no guarantee that the request is valid until then. Once
* you start collecting the flow, the SDK will lazily load response pages by making service calls until there are no
* pages left or the flow is cancelled. If there are errors in your request, you will see the failures only after you start
* collection.
*
* When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service
* calls are made until the flow is collected. This also means there is no guarantee that the request is valid
* until then. Once you start collecting the flow, the SDK will lazily load response pages by making service
* calls until there are no pages left or the flow is cancelled. If there are errors in your request, you will
* see the failures only after you start collection.
* @param initialRequest A [ListFunctionsRequest] to start pagination
* @return A [kotlinx.coroutines.flow.Flow] that can collect [ListFunctionsResponse]
*/
Expand All @@ -165,6 +166,20 @@ class PaginatorGeneratorTest {
emit(result)
}
}

/**
* Paginate over [ListFunctionsResponse] results.
*
* When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service
* calls are made until the flow is collected. This also means there is no guarantee that the request is valid
* until then. Once you start collecting the flow, the SDK will lazily load response pages by making service
* calls until there are no pages left or the flow is cancelled. If there are errors in your request, you will
* see the failures only after you start collection.
* @param block A builder block used for DSL-style invocation of the operation
* @return A [kotlinx.coroutines.flow.Flow] that can collect [ListFunctionsResponse]
*/
fun TestClient.listFunctionsPaginated(block: ListFunctionsRequest.Builder.() -> Unit): Flow<ListFunctionsResponse> =
listFunctionsPaginated(ListFunctionsRequest.Builder().apply(block).build())
""".trimIndent()

actual.shouldContainOnlyOnceWithDiff(expected)
Expand All @@ -182,11 +197,12 @@ class PaginatorGeneratorTest {
val expectedCode = """
/**
* Paginate over [ListFunctionsResponse] results.
* When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service calls are
* made until the flow is collected. This also means there is no guarantee that the request is valid until then. Once
* you start collecting the flow, the SDK will lazily load response pages by making service calls until there are no
* pages left or the flow is cancelled. If there are errors in your request, you will see the failures only after you start
* collection.
*
* When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service
* calls are made until the flow is collected. This also means there is no guarantee that the request is valid
* until then. Once you start collecting the flow, the SDK will lazily load response pages by making service
* calls until there are no pages left or the flow is cancelled. If there are errors in your request, you will
* see the failures only after you start collection.
* @param initialRequest A [ListFunctionsRequest] to start pagination
* @return A [kotlinx.coroutines.flow.Flow] that can collect [ListFunctionsResponse]
*/
Expand All @@ -206,6 +222,20 @@ class PaginatorGeneratorTest {
}
}

/**
* Paginate over [ListFunctionsResponse] results.
*
* When this operation is called, a [kotlinx.coroutines.Flow] is created. Flows are lazy (cold) so no service
* calls are made until the flow is collected. This also means there is no guarantee that the request is valid
* until then. Once you start collecting the flow, the SDK will lazily load response pages by making service
* calls until there are no pages left or the flow is cancelled. If there are errors in your request, you will
* see the failures only after you start collection.
* @param block A builder block used for DSL-style invocation of the operation
* @return A [kotlinx.coroutines.flow.Flow] that can collect [ListFunctionsResponse]
*/
fun TestClient.listFunctionsPaginated(block: ListFunctionsRequest.Builder.() -> Unit): Flow<ListFunctionsResponse> =
listFunctionsPaginated(ListFunctionsRequest.Builder().apply(block).build())

/**
* This paginator transforms the flow returned by [listFunctionsPaginated]
* to access the nested member [FunctionConfiguration]
Expand Down