Skip to content

feat(typespec-go): support paging with a relative nextLink - #5079

Open
tadelesh wants to merge 1 commit into
mainfrom
tadelesh/go-relative-nextlink
Open

feat(typespec-go): support paging with a relative nextLink#5079
tadelesh wants to merge 1 commit into
mainfrom
tadelesh/go-relative-nextlink

Conversation

@tadelesh

Copy link
Copy Markdown
Member

Resolves #4994.

What

Adds support for paging with a relative nextLink to the Go emitter, and enables the previously disabled azure/core/page withRelativeNextLink Spector test.

Problem

runtime.FetcherForNextLink builds the follow-up request with runtime.NewRequest(ctx, verb, nextLink), which rejects a non-absolute URL with no Host in request URL. A service that returns a next link relative to its endpoint (e.g. /azure/core/page/with-relative-next-link/page/2, per the Spector scenario) therefore failed on the second page. This is why TestPageClient_NewWithRelativeNextLinkPager was commented out with a TODO: runtime.FetcherForNextLink doesn't support relative next link URLs.

The code model and codegen already carry everything needed (PageableStrategyNextLink + the nextLink field path); only the emitted fetcher was missing the resolution step. Relative-ness is a property of the runtime value, not something TCGC can express statically, so the check has to be in generated code.

Fix

In packages/typespec-go/src/codegen/core/operations.ts, emitPagerDefinition now emits a resolution step before calling runtime.FetcherForNextLink:

nextLink := ""
if page != nil {
    nextLink = *page.NextLink
}
// the service can return a next link that's relative to the endpoint, however
// runtime.FetcherForNextLink requires an absolute URL, so resolve it here.
if nextLink != "" {
    if u, err := url.Parse(nextLink); err == nil && !u.IsAbs() {
        nextLink = runtime.JoinPaths(client.endpoint, nextLink)
    }
}
resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, ...)

Notes:

  • The endpoint expression is client.<endpointParam> for data-plane clients and client.internal.Endpoint() for ARM clients, matching what *CreateRequest already uses.
  • runtime.JoinPaths is the same helper every generated *CreateRequest uses to combine the endpoint with a path, so relative links are resolved consistently with first-page requests (this also matches how the Python/JS emitters append a relative next link to the endpoint). It preserves the next link's query string.
  • Absolute next links take the u.IsAbs() fast path and are passed through byte-for-byte, so behavior for compliant services is unchanged.
  • Skipped when the pager uses a custom next page operation (FetcherForNextLinkOptions.NextReq), since that operation builds the request (and joins the endpoint) itself, and when the client has no single endpoint (the Autorest-only templatedHost compat case).
  • For LRO pagers the next link is now hoisted into a local so it can be resolved.

Tests

  • Enabled TestPageClient_NewWithRelativeNextLinkPager in test/azure-http-specs/azure/core/azurepagegroup/custom_client_test.go; it passes against the Spector mock server (2 pages, second fetched via the relative link).
  • Updated the pageable-lro and tenant-resource unit scenario snapshots.
  • Validated: pnpm vitest run (49 unit tests pass); full regeneration of all Spector modules followed by pnpm go-test — all generated modules pass.

runtime.FetcherForNextLink requires an absolute URL, so a service that
returns a next link relative to its endpoint (e.g. "/page/2") failed with
"no Host in request URL". Generated pagers now resolve a relative next
link against the client endpoint before fetching the next page; absolute
next links are passed through unmodified.

Enables the previously disabled azure/core/page withRelativeNextLink
Spector test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a2fed377-7b26-45fc-b3aa-8b6d6df8b46a
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:go Issues for @azure-tools/typespec-go emitter label Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @azure-tools/typespec-go
Show changes

@azure-tools/typespec-go - feature ✏️

Support paging with a relative nextLink. Pagers now resolve a next link that's relative to the client endpoint before fetching the next page; absolute next links are unchanged.

@github-actions

Copy link
Copy Markdown
Contributor

Go emitter diff

Baseline gh:f4ad548c39ac22e47f1f98e574b774fb61460fbd vs this PR.

Diff summary: 18 file(s), +204 / -5

Rendered diff: inline on the run summary, or the emitter-diff-go-html artifact.

Informational check (core/eng/emitter-diff); does not block the PR.

@pkg-pr-new

pkg-pr-new Bot commented Jul 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-go@5079

commit: a8fd511

@tadelesh

Copy link
Copy Markdown
Member Author

@jhendrixMSFT I remembered that you said we already support it in swagger cases. But I could not find the related code. Current implementation will change all the generated code, which I do not prefer. Any suggestions?

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

Fetcher: func(ctx context.Context, page *PageableLROsClientListPrivateEndPointsResponse) (PageableLROsClientListPrivateEndPointsResponse, error) {
resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), *page.NextLink, func(ctx context.Context) (*policy.Request, error) {
nextLink := *page.NextLink
// the service can return a next link that's relative to the endpoint, however

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We shouldn't be emitting comments.

// the service can return a next link that's relative to the endpoint, however
// runtime.FetcherForNextLink requires an absolute URL, so resolve it here.
if nextLink != "" {
if u, err := url.Parse(nextLink); err == nil && !u.IsAbs() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A few problems here.

  • err is discarded when not nil
  • we parse just to check IsAbs() then throw that away
    • this happens for all paths and IIRC relative paths are rare

Do we know at codegen time if the path is relative?

@tadelesh tadelesh Jul 29, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, we could not know that. It's kind of a runtime behavior. I remembered that you said we already support it in swagger cases. But I could not find the related code. Current implementation will change all the generated code, which I do not prefer. Any suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:go Issues for @azure-tools/typespec-go emitter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support paging with relative nextlink

2 participants