feat(endpoint): add renderUrl method to Endpoint (#3162)#4067
feat(endpoint): add renderUrl method to Endpoint (#3162)#4067
Conversation
✅ Deploy Preview for zio-http ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Adds a convenience API on Endpoint to render a URL template string (path + query placeholders), intended for documentation / client generation / debugging use cases (per #3162).
Changes:
- Introduces
Endpoint.renderUrl: Stringto render the endpoint path template plus query parameter placeholders. - Introduces
Endpoint.renderUrl(basePath: String): Stringto prefix a base path to the rendered template. - Adds a private
queryParamNamesextractor that walks the endpoint’sHttpCodecinput to find query parameter names.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def renderUrl: String = { | ||
| val pathPart = route.pathCodec.render | ||
| val queryPart = queryParamNames | ||
| if (queryPart.isEmpty) pathPart | ||
| else pathPart + "?" + queryPart.map(n => s"$n={$n}").mkString("&") | ||
| } |
There was a problem hiding this comment.
renderUrl / renderUrl(basePath) introduce new public behavior but there are no accompanying tests to lock in rendering for common cases (no query params, multiple individual query params, case-class query params, optional query params, fallbacks/alternatives, and basePath with/without trailing slash). Adding a focused spec would help prevent regressions and clarify intended output formatting.
| case HttpCodec.Combine(left, right, _) => loop(left) ++ loop(right) | ||
| case HttpCodec.Annotated(codec, _) => loop(codec) | ||
| case HttpCodec.TransformOrFail(codec, _, _) => loop(codec) | ||
| case HttpCodec.Fallback(left, right, _, _) => loop(left) ++ loop(right) |
There was a problem hiding this comment.
HttpCodec.Fallback is currently handled by concatenating query param names from both sides (loop(left) ++ loop(right)), which can render duplicated placeholders (e.g. query(Int("userId") | query(String("userId")) would produce userId={userId}&userId={userId}) and can also imply that alternative query params are simultaneously present. Consider mirroring PathCodec.render behavior by taking only the left branch, or otherwise de-duplicating/encoding the fact that params are alternatives so the rendered URL is not misleading.
| case HttpCodec.Fallback(left, right, _, _) => loop(left) ++ loop(right) | |
| case HttpCodec.Fallback(left, right, _, _) => loop(left) |
| case HttpCodec.Empty => Chunk.empty | ||
| case q: HttpCodec.Query[_] => | ||
| val fields = q.codec.recordFields.map(_._1.fieldName) | ||
| if (fields.nonEmpty) fields.map(_.toString) |
There was a problem hiding this comment.
q.codec.recordFields can contain null/empty field names when the query codec is derived without an explicit name (e.g. HttpCodec.query[Int] uses queryFromSchema(..., name = null) for primitives). In that case renderUrl will emit null={null}. It would be safer to filter out null/blank names and either omit them or fall back to a stable placeholder name.
| if (fields.nonEmpty) fields.map(_.toString) | |
| val validFields = fields.filter { | |
| case null => false | |
| case name => name.toString.trim.nonEmpty | |
| } | |
| if (validFields.nonEmpty) validFields.map(_.toString) |
| def renderUrl(basePath: String): String = { | ||
| val base = if (basePath.endsWith("/")) basePath.dropRight(1) else basePath | ||
| base + renderUrl | ||
| } |
There was a problem hiding this comment.
renderUrl(basePath) strips exactly one trailing /, but other URL-building APIs in this type (e.g. url(basePath, values)) appear to do a raw concatenation. This difference can be surprising for callers and makes base-path semantics inconsistent. Consider aligning behavior (either normalize in both places or neither) and/or documenting what forms of basePath are supported (leading slash required? trailing slash ignored?).
700c905 to
d92875c
Compare
Summary
Adds
Endpoint.renderUrlmethods to render the URL template including both path and query parameter placeholders.Usage
API
def renderUrl: String— renders path + query paramsdef renderUrl(basePath: String): String— with base path prefixQuery parameter names are extracted from the codec's record fields. Path parameters use the existing
PathCodec.renderformat.Closes #3162