Conversation
`ResolvedUrl` is the two projections of a resolved REST endpoint: `url()` returns the request URL to fetch, and `canonical_route_key()` returns the origin-less `path[?query]` that `@wordpress/api-fetch`'s preload middleware matches on. The key uses a memoized canonical route path plus the request URL's query with `rest_route` dropped, so it is byte-identical on pretty (`…/wp-json/…`) and plain (`…?rest_route=…`) permalink sites and omits the `?` when there is no query. `by_appending_query_pairs` attaches endpoint query parameters while preserving that path. Not yet returned by `resolve`; that comes next.
`resolve` now returns `Arc<ResolvedUrl>` instead of `Arc<ParsedUrl>` across the trait and all three resolvers (`WpOrgSiteApiUrlResolver`, `WpComDotOrgApiUrlResolver`, `WpComApiClientInternalUrlResolver`), each stamping the value with the root it used and the canonical route path its own `route_path()` produces. Internal call sites — the endpoint-builder macro funnel and the resolver tests — unwrap to the request URL with `.url()`, so request-building is unchanged and the plain-permalinks and index-self-href golden URL tables still pass verbatim. This is a breaking change to the `with_foreign` trait: apps with custom `ApiUrlResolver` implementations must return a `ResolvedUrl`.
New UniFFI objects are invisible to the Swift package's consumers without a `public typealias` in `Exports.swift`; only `swift test` catches the omission, not static review.
`ResolvedUrl.url()` now mirrors `ParsedUrl.url()` — it returns the request URL as a string rather than a `ParsedUrl` — and the new `parsed_url()` is the escape hatch to the `ParsedUrl` object. This fixes the inconsistency where `.url()` meant different things on the two types, and keeps the `resolve(...).url()` call pattern returning the same string it did when `resolve` returned a `ParsedUrl`, so that call site is unaffected by the switch to `ResolvedUrl`. The endpoint-builder macro funnel and the resolver tests that need the `ParsedUrl` object use `.parsed_url()`.
`as_str()` returns the request URL as a borrowed `&str` — the Rust-side, zero-copy counterpart to `url()`, mirroring `ParsedUrl::as_str()`. Like `ParsedUrl`'s, it lives in a plain (non-`#[uniffi::export]`) impl, so it's Rust-only and leaves the FFI surface unchanged; Swift/Kotlin still use `url()`.
…wrap The inline token-stream snapshots in `helpers_to_generate_tokens.rs` assert the exact code the endpoint-builder macro emits. The funnel now unwraps the resolved URL via `.parsed_url()` before taking `.inner`, so the six `test_fn_body_get_url_from_api_root_url` cases still expected the pre-change `resolve(...)).inner` form. Updates them to the `resolve(...).parsed_url()).inner` form. These run in the `test-rust-wp-derived-request-parser` and `test-rust-lib` CI jobs (`cargo test --package wp_derive_request_builder` / `cargo test --lib`), which `cargo test -p wp_api --lib` doesn't cover.
Collaborator
XCFramework BuildThis PR's XCFramework is available for testing. Add to your .package(url: "https://github.com/automattic/wordpress-rs", branch: "pr-build/1599")Built from 57213d5 |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Evolves the REST URL resolver so
ApiUrlResolver.resolvereturns aResolvedUrlinstead of a bareParsedUrl.ResolvedUrlcarries both halves a preloading editor needs: the request URL to fetch (url()) and the canonical, origin-less route key that@wordpress/api-fetch's preload middleware matches on (canonical_route_key()). This lets wprs own the preload cache key natively instead of asking GutenbergKit to re-implement api-fetch'snormalizePath.Stacked on #1549 (
issue-1543), which shipsParsedUrl.by_appending_query_pairs— the primitiveResolvedUrlbuilds on. This targetsissue-1543, so #1549 becomes theResolvedUrlPR.Why
canonical_route_keyapi-fetchmatches an outgoing request to a preload key by running both throughnormalizePath, which is origin-less, keys on path + query, sorts query params on both sides, unwraps the?rest_route=form back to the canonical/wp/v2/…path, and early-returns when there's no query. So the key GBK precomputes natively is the canonical/wp/v2/themes?context=edit&status=active— not the full request URL (origin isn't stripped) and not a query-less path (won't match a query-bearing request).by_appending_query_pairsgave GBK the request URL but nothing for the key;route_pathgave a query-less path.ResolvedUrlcloses that gap.Changes
wp_api/src/resolved_url.rs: newResolvedUrl(uniffi::Object) withurl()(the request URL as a string, mirroringParsedUrl.url()),parsed_url()(the same as aParsedUrlobject — the escape hatch),by_appending_query_pairs(), andcanonical_route_key(), plus an exported constructor so foreignApiUrlResolverimpls can build one. It carries the request URL, the API root it was resolved against, and a memoized canonical route path — so the key is a pure concat, guaranteed to matchroute_path().ApiUrlResolver::resolvenow returnsArc<ResolvedUrl>across the trait and all three resolvers (WpOrgSiteApiUrlResolver,WpComDotOrgApiUrlResolver,WpComApiClientInternalUrlResolver), each stamping the value with the root it used and its ownroute_path.wp_derive_request_builder) and the other internal call sites unwrap to the request URL with.url(), so request-building is byte-identical.canonical_route_key(): origin-lesspath[?query]; unwraps?rest_route=back to/wp/v2/…; dropsrest_route; omits the?when there is no query. Invariant: byte-identical on pretty (…/wp-json/…) and plain (…?rest_route=…) permalink sites.public typealias ResolvedUrlinExports.swift(new UniFFI objects are invisible to Swift consumers without it; onlyswift testcatches the omission). Kotlin needs no re-export.canonical_route_key).BREAKING
ApiUrlResolver.resolvenow returnsResolvedUrlinstead ofParsedUrl. This is awith_foreigntrait change — the accepted trade-off for wprs owning the cache key.resolve(...).url()still returns the request URL as a string, so that call pattern is unchanged; the return type is what differs. Apps with a customApiUrlResolvermust return aResolvedUrl(build one withResolvedUrl(url:apiRoot:routePath:)), and code that used the resolved value as aParsedUrlobject readsresolve(...).parsed_url().Test plan
cargo test -p wp_api --lib— 1967 pass, including newResolvedUrlunit tests and a resolver round-trip proving the pretty/plain key invariant (no-query, multi-param, encoded value, embedded-slash route)plain_permalinks_url_tests(45) andindex_self_href_url_tests(30) pass unchanged — the funnel change did not alter request-buildingcanonical_route_key()equals its pretty-permalinks counterpart across real route shapescargo clippy --tests --all-targets --all-features -- -D warningsclean;cargo fmt --all -- --checkcleanmake xcframework-only-macos && swift build --target WordPressAPI—Exports.swiftcompiles against the regenerated bindingsBUILDKITE=1 swift test— 82 tests in 18 suites passxcrun swift format lint --strictcleancd native/kotlin && ./gradlew :api:kotlin:compileKotlin :api:kotlin:detekt— BUILD SUCCESSFULChangelog
CHANGELOG.mdunder## [Unreleased], using the Keep a Changelog categories (Added,Changed,Deprecated,Removed,Fixed,Security). Prefix breaking changes with**BREAKING:**.