Skip to content

KTOR-8573 Improvements for "StringValuesBuilder#appendAll" #4870

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

Merged
merged 12 commits into from
Jun 9, 2025

Conversation

otobrglez
Copy link
Contributor

@otobrglez otobrglez commented May 20, 2025

Hey!

While using Ktor, I found that it could be more ergonomic if StringValuesBuilder also supported mutations with maps. This is my humble improvement that could make this experience better.

Subsystem
ktor-utils

Motivation
KTOR-8573 More overloads for StringValuesBuilder.appendAll

I wanted to build URLs with something like this:

val builder = URLBuilder("https://github.com").apply {
    parameters.appendAll(mapOf("foo" to "bar", "baz" to "qux"))
    parameters.appendAllIterable(mapOf("test" to listOf("1", "2", "3")))
}

println(builder.buildString())
// => https://github.com?foo=bar&baz=qux&test=1&test=2&test=3

Solution

  • My PR adds two methods named appendAll to StringValuesBuilder inside StringValues.kt
  • One method is for Map<String, String> and another for Map<String, Iterable<String>>
  • Classes that implement this interface get these abilities for free as the existing mechanisms are reused (such as append and appendAll)

P.S.: Please be gentle, as this is my first PR to Ktor.

Regards

- Oto

Copy link
Contributor

coderabbitai bot commented May 20, 2025

Walkthrough

This change introduces four new appendAll extension functions for StringValuesBuilder in the Ktor utilities, enabling bulk addition of key-value pairs from various data structures. Corresponding tests are added to verify the new functionality. API files are updated to reflect the new public methods.

Changes

Files/Groups Change Summary
ktor-utils/common/src/io/ktor/util/StringValues.kt Added four appendAll extension functions for StringValuesBuilder to support bulk appending from pairs and maps. Reordered a method.
ktor-utils/common/test/io/ktor/util/StringValuesTest.kt Added a test function to verify the new appendAll extension functions' behavior.
ktor-utils/api/ktor-utils.api,
ktor-utils/api/ktor-utils.klib.api
Declared the new appendAll extension functions in the public API, exposing them for various input types and updating method lists.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f53333 and 811ab12.

📒 Files selected for processing (1)
  • ktor-utils/api/ktor-utils.api (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • ktor-utils/api/ktor-utils.api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

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

Looks good, could you please run ./gradlew apiDump and commit the result to update the API snapshot?

Copy link
Member

@osipxd osipxd left a comment

Choose a reason for hiding this comment

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

Hey @otobrglez, do you want to continue work on this PR?
We're preparing release 3.2.0 this week so it is the best time to include new APIs.

@otobrglez
Copy link
Contributor Author

Hey @otobrglez, do you want to continue work on this PR? We're preparing release 3.2.0 this week so it is the best time to include new APIs.

Yeah, I would love to. Please guide me. 😅

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
ktor-utils/common/test/io/ktor/util/StringValuesTest.kt (1)

120-135: Enhance test assertions for comprehensive validation.

The test covers all four new appendAll extension functions, which is excellent. However, the assertions could be more thorough to ensure all values are correctly stored:

Consider enhancing the assertions to verify all values:

     assertTrue { map.contains("foo") }
     assertTrue { map.contains("foo", "bar") }
     assertTrue { map.contains("baz") }
+    assertTrue { map.contains("baz", "bar") }
+    assertTrue { map.contains("baz", "bor") }
+    assertEquals(listOf("bar", "bor"), map.getAll("baz"))
     assertTrue { map.contains("rar") }
+    assertTrue { map.contains("rar", "boo") }
+    assertTrue { map.contains("rar", "too") }
+    assertEquals(listOf("boo", "too"), map.getAll("rar"))
     assertTrue { map.contains("goo", "boo") }
     assertTrue { map.contains("doo", "dah") }

This follows the pattern established by other tests in the file (like twoValueMap and threeValueCaseInsensitiveMap) that use both contains() and getAll() to verify multi-value scenarios.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef228c5 and 7c99cc8.

📒 Files selected for processing (2)
  • ktor-utils/common/src/io/ktor/util/StringValues.kt (3 hunks)
  • ktor-utils/common/test/io/ktor/util/StringValuesTest.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ktor-utils/common/src/io/ktor/util/StringValues.kt
🧰 Additional context used
🧬 Code Graph Analysis (1)
ktor-utils/common/test/io/ktor/util/StringValuesTest.kt (1)
ktor-utils/common/src/io/ktor/util/StringValues.kt (9)
  • appendAll (117-117)
  • appendAll (118-118)
  • appendAll (258-262)
  • appendAll (270-275)
  • appendAll (441-445)
  • appendAll (475-477)
  • appendAll (487-490)
  • appendAll (500-503)
  • appendAll (513-516)

@otobrglez
Copy link
Contributor Author

Hey!

  • @osipxd I've implemented it the way you've proposed
  • @bjhham I've run the apiDump task (my MacBook almost ran out of the room on its spinning fans)

I hope this will do well.

Regards,

- Oto

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c99cc8 and 8be2fa7.

📒 Files selected for processing (4)
  • ktor-http/api/ktor-http.api (1 hunks)
  • ktor-utils/api/ktor-utils.api (2 hunks)
  • ktor-utils/api/ktor-utils.klib.api (1 hunks)
  • ktor-utils/common/test/io/ktor/util/StringValuesTest.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ktor-utils/common/test/io/ktor/util/StringValuesTest.kt
🔇 Additional comments (3)
ktor-utils/api/ktor-utils.api (1)

395-398: LGTM! Core functionality aligns with PR objectives.

The addition of default implementations for appendAll methods with Map parameters supports the stated goal of enabling bulk parameter appending from maps.

ktor-utils/api/ktor-utils.klib.api (1)

943-946: LGTM! Excellent ergonomic improvements to StringValuesBuilder.

These four new appendAll extension functions provide comprehensive support for bulk appending operations from various collection types. The implementation correctly:

  • Supports both Map<String, String> and Map<String, Iterable<String>> for different use cases
  • Provides vararg overloads for Pair<String, String> and Pair<String, Iterable<String>>
  • Returns StringValuesBuilder for method chaining
  • Follows consistent naming conventions with existing appendAll methods

This enhancement will significantly improve the developer experience when building URL parameters from maps, exactly as described in the PR objectives.

ktor-http/api/ktor-http.api (1)

831-834:

✅ Verification successful

Verify method signatures for appendAll overloads.

Both appendAll and appendAllIterable methods have identical JVM signatures (Lio/ktor/http/ParametersBuilder;Ljava/util/Map;)V, which raises concerns about method resolution and API design.

According to the PR objectives, the intent is to support:

  • Map<String, String>
  • Map<String, Iterable<String>>

However, due to type erasure, both would have the same erased signature. Please verify:

  1. Method Differentiation: How are these methods distinguished at runtime when both accept Map with the same erased signature?

  2. Naming Convention: The appendAllIterable name seems verbose compared to standard Kotlin overloading patterns. Is this compiler-generated name mangling, or was this intentionally chosen?

  3. API Consistency: Ensure this aligns with the corresponding StringValuesBuilder$DefaultImpls mentioned in the AI summary.

Let me verify the actual source code to understand how these methods are differentiated:


🏁 Script executed:

#!/bin/bash
# Search for ParametersBuilder appendAll implementations to understand the actual method signatures
ast-grep --pattern 'fun appendAll($$$) {
  $$$
}'

# Also search for any StringValuesBuilder DefaultImpls for comparison
rg -A 5 "DefaultImpls" 

# Look for the actual extension function declarations
ast-grep --pattern 'fun ParametersBuilder.appendAll($$$) {
  $$$
}'

Length of output: 28971


DefaultImpls methods are correctly differentiated

  • The Kotlin compiler emits two distinct static methods—appendAll and appendAllIterable—to prevent JVM signature clashes after type erasure.
  • This naming pattern exactly mirrors the implementation in StringValuesBuilder$DefaultImpls, ensuring consistency across the API.

No changes required.

Copy link
Member

@osipxd osipxd left a comment

Choose a reason for hiding this comment

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

Thank you for the quick fix! Well done! I have only a few minor comments.
To keep your laptop in the room, try ./gradlew :ktor-utils:apiDump 🙂

@otobrglez
Copy link
Contributor Author

Hey!

I believe I've addressed all the issues that were identified. Hopefully, this is ready now?

Regards,

- Oto

Copy link
Member

@osipxd osipxd left a comment

Choose a reason for hiding this comment

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

LGTM 👍
BCV still generates wrong API dumps, but I'll fix it myself.
Thank you!

@osipxd osipxd changed the title Improvements for "StringValuesBuilder#appendAll" KTOR-8573 Improvements for "StringValuesBuilder#appendAll" Jun 9, 2025
@osipxd osipxd enabled auto-merge (squash) June 9, 2025 09:47
@osipxd osipxd merged commit bde9f1e into ktorio:main Jun 9, 2025
15 of 17 checks passed
@otobrglez
Copy link
Contributor Author

I appreciate your support! Cheers!

@otobrglez otobrglez deleted the improvements-string-value-builder branch June 9, 2025 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants