Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
107 changes: 99 additions & 8 deletions src/libs/AssemblyAI/Generated/AssemblyAI.AutoSDKPager.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,85 @@ namespace AssemblyAI
/// </summary>
public static class AutoSDKPager
{
/// <summary>Copies request options for one generated next-URL page request.</summary>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::AssemblyAI.AutoSDKRequestOptions CreatePageRequestOptions(
global::AssemblyAI.AutoSDKRequestOptions? source,
string? nextUrl)
{
var clone = new global::AssemblyAI.AutoSDKRequestOptions();
if (source is not null)
{
foreach (var header in source.Headers)
{
clone.Headers[header.Key] = header.Value;
}
foreach (var query in source.QueryParameters)
{
clone.QueryParameters[query.Key] = query.Value;
}
clone.Timeout = source.Timeout;
clone.Retry = source.Retry;
clone.ReadResponseAsString = source.ReadResponseAsString;
clone.Authorizations = source.Authorizations;
}
clone.PaginationUrl = nextUrl;
return clone;
}

/// <summary>Returns a validated next-page URL, or null for an ordinary request.</summary>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static string? ResolvePageUrl(
string path,
global::AssemblyAI.AutoSDKRequestOptions? requestOptions,
global::System.Uri? baseAddress)
{
var nextUrl = requestOptions?.PaginationUrl;
if (string.IsNullOrWhiteSpace(nextUrl))
{
return null;
}

if (baseAddress is null &&
global::System.Uri.TryCreate(path, global::System.UriKind.Absolute, out var pathUri) &&
(pathUri.Scheme == global::System.Uri.UriSchemeHttp || pathUri.Scheme == global::System.Uri.UriSchemeHttps))
{
baseAddress = pathUri;
}
if (baseAddress is not null)
{
var resolved = new global::System.Uri(baseAddress, nextUrl!);
EnsureSameOrigin(resolved.AbsoluteUri, baseAddress);
return resolved.AbsoluteUri;
}
if (global::System.Uri.TryCreate(nextUrl, global::System.UriKind.Absolute, out _))
{
throw new global::System.InvalidOperationException(
"Cannot follow an absolute next-page URL without a known base address.");
}

return nextUrl;
}

/// <summary>Uses the HTTP client's origin, or the operation server when it has one.</summary>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::System.Uri? GetPaginationBaseAddress(
global::System.Uri? clientBaseAddress,
string operationBaseUrl)
{
if (clientBaseAddress is not null)
{
return clientBaseAddress;
}

return global::System.Uri.TryCreate(
operationBaseUrl,
global::System.UriKind.Absolute,
out var serverBaseAddress)
? serverBaseAddress
: null;
}

/// <summary>
/// Paginates through offset/page-number-style endpoints. Calls <paramref name="fetchPage"/>
/// repeatedly with an incrementing page number until the page returns no items, or the
Expand Down Expand Up @@ -74,7 +153,7 @@ public static class AutoSDKPager
/// <summary>
/// Paginates through cursor/next-page-token-style endpoints. Calls <paramref name="fetchPage"/>
/// with the cursor returned by the previous page until <paramref name="extractNextCursor"/>
/// returns null or whitespace.
/// returns null or whitespace, or a cursor repeats.
/// </summary>
/// <typeparam name="TPage"></typeparam>
/// <typeparam name="TItem"></typeparam>
Expand All @@ -96,9 +175,14 @@ public static class AutoSDKPager
extractNextCursor = extractNextCursor ?? throw new global::System.ArgumentNullException(nameof(extractNextCursor));

var cursor = initialCursor;
var seenCursors = new global::System.Collections.Generic.HashSet<string>(global::System.StringComparer.Ordinal);
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(cursor) && !seenCursors.Add(cursor!))
{
yield break;
}

var response = await fetchPage(cursor, cancellationToken).ConfigureAwait(false);
if (response is null)
Expand Down Expand Up @@ -156,9 +240,14 @@ public static class AutoSDKPager
}

var url = initialUrl;
var seenUrls = new global::System.Collections.Generic.HashSet<string>(global::System.StringComparer.Ordinal);
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(url) && !seenUrls.Add(url!))
{
yield break;
}

var response = await fetchPage(url, cancellationToken).ConfigureAwait(false);
if (response is null)
Expand Down Expand Up @@ -224,7 +313,7 @@ public static class AutoSDKPager
/// Paginates through APIs that embed an absolute <c>next</c> URL in the response body
/// (Firecrawl, Slack, Linear, Notion, HubSpot, etc.). Calls <paramref name="fetchPage"/>
/// with the URL returned by <paramref name="extractNextUrl"/> from the previous page
/// until the extractor returns null/whitespace.
/// until the extractor returns null/whitespace or a URL repeats.
///
/// <para><b>Security:</b> when <paramref name="baseAddress"/> is provided, every next-URL
/// origin must match it before the next fetch runs. An unchecked walker would let a
Expand Down Expand Up @@ -254,12 +343,17 @@ public static class AutoSDKPager
extractNextUrl = extractNextUrl ?? throw new global::System.ArgumentNullException(nameof(extractNextUrl));

var url = initialUrl;
var seenUrls = new global::System.Collections.Generic.HashSet<string>(global::System.StringComparer.Ordinal);
while (true)
{
cancellationToken.ThrowIfCancellationRequested();

if (!string.IsNullOrWhiteSpace(url))
{
if (!seenUrls.Add(url!))
{
yield break;
}
EnsureSameOrigin(url!, baseAddress);
}

Expand Down Expand Up @@ -291,7 +385,8 @@ public static class AutoSDKPager
/// <summary>
/// Validates that <paramref name="nextUrl"/> shares an origin with <paramref name="baseAddress"/>.
/// Throws when they differ so an embedded foreign URL can't redirect the SDK (and the
/// caller's <c>Authorization</c> header) to a hostile host. When <paramref name="baseAddress"/>
/// caller's <c>Authorization</c> header) to a hostile host. Relative URLs are resolved
/// before comparison, including protocol-relative URLs. When <paramref name="baseAddress"/>
/// is null, validation is skipped — callers should pass <c>HttpClient.BaseAddress</c>.
/// </summary>
public static void EnsureSameOrigin(string nextUrl, global::System.Uri? baseAddress)
Expand All @@ -301,11 +396,7 @@ public static void EnsureSameOrigin(string nextUrl, global::System.Uri? baseAddr
return;
}

if (!global::System.Uri.TryCreate(nextUrl, global::System.UriKind.Absolute, out var absolute))
{
// Relative URLs are safe — they're resolved against the client's BaseAddress on send.
return;
}
var absolute = new global::System.Uri(baseAddress, nextUrl);

if (!string.Equals(absolute.Scheme, baseAddress.Scheme, global::System.StringComparison.OrdinalIgnoreCase) ||
!string.Equals(absolute.Host, baseAddress.Host, global::System.StringComparison.OrdinalIgnoreCase) ||
Expand Down
3 changes: 3 additions & 0 deletions src/libs/AssemblyAI/Generated/AssemblyAI.OptionsSupport.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ private static void ApplyAuthorization(
/// </summary>
public sealed class AutoSDKRequestOptions
{
// Set only by AutoSDKPager on a generated next-URL paging call.
internal string? PaginationUrl { get; set; }

/// <summary>
/// Additional headers applied after generated and client-level headers.
/// </summary>
Expand Down
Loading