Skip to content

feat: uniformize SDK method parameters with Options/Request pattern#11

Merged
devRael1 merged 6 commits into
masterfrom
copilot/refactor-sdk-parameter-management
Apr 7, 2026
Merged

feat: uniformize SDK method parameters with Options/Request pattern#11
devRael1 merged 6 commits into
masterfrom
copilot/refactor-sdk-parameter-management

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 7, 2026

  • Create 11 new Options models for parameter uniformization
  • Wire existing ExportDataOptions into ITorrentsClient
  • Move provider into OAuthRegisterRequest with [JsonIgnore]
  • Update 8 interfaces + implementations to Options/Request pattern
  • Update 9 unit test files with 17 new validation tests
  • Update 7 integration test files
  • Update 13 example files
  • Comprehensive dev agent code review (4 agents, all APPROVED)
  • Apply PR review feedback: validate options.Hashes in CheckCachedAsync (3 clients)
  • Apply PR review feedback: fix XML exception docs for ArgumentNullException vs ArgumentException across 8 interfaces
  • Add 3 new null-Hashes validation tests + fix test naming accuracy
  • Build: 0 warnings, 0 errors across net6.0–net10.0
  • Unit tests: 321 passing, Integration tests: 42 (3 pass, 39 skip)

Copilot AI and others added 4 commits April 7, 2026 12:02
… parameter uniformization

- Add shared GetMyListOptions and CheckCachedOptions in Models/Common/
- Add GetTorrentInfoOptions, CreateStreamOptions, GetStreamDataOptions,
  GetQueuedOptions, GetIntercomHashOptions, DownloadUsenetOptions,
  SearchTorznabOptions, SearchNewznabOptions, CheckInactiveOptions
- Wire existing ExportDataOptions into ITorrentsClient
- Update all 8 affected interfaces and implementations
- Move provider into OAuthRegisterRequest (JsonIgnore)
- SDK builds with 0 warnings, 0 errors

Agent-Logs-Url: https://github.com/devRael1/TorBoxSDK/sessions/70573361-3d28-4a9f-bf72-8f15e1134353

Co-authored-by: devRael1 <91017912+devRael1@users.noreply.github.com>
Migrate all example files from inline named parameters to the new
Options object pattern introduced during the SDK refactor:

- GetMyTorrentListAsync/GetMyUsenetListAsync → GetMyListOptions
- CheckCachedAsync → CheckCachedOptions
- GetTorrentInfoAsync → GetTorrentInfoOptions
- ExportDataAsync → ExportDataOptions
- GetQueuedAsync → GetQueuedOptions
- CreateStreamAsync → CreateStreamOptions
- GetStreamDataAsync → GetStreamDataOptions
- GetIntercomHashAsync → GetIntercomHashOptions
- OAuthRegisterAsync → Provider on OAuthRegisterRequest
- CheckForInactiveAsync → CheckInactiveOptions
- SearchTorznabAsync → SearchTorznabOptions
- SearchNewznabAsync → SearchNewznabOptions
- DownloadUsenetAsync → DownloadUsenetOptions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: devRael1 <91017912+devRael1@users.noreply.github.com>
- Add 3 missing GetIntercomHashAsync null/empty validation tests
- Add 1 missing CheckForInactiveAsync empty AuthId test
- Add 6 missing TorrentsClient null Options validation tests
- Add 4 missing IntegrationsClient empty provider validation tests
- Fix var usage in RelayApiClientTests to use explicit types
- Unit tests: 315 passed (up from 301)

Agent-Logs-Url: https://github.com/devRael1/TorBoxSDK/sessions/70573361-3d28-4a9f-bf72-8f15e1134353

Co-authored-by: devRael1 <91017912+devRael1@users.noreply.github.com>
@devRael1
Copy link
Copy Markdown
Owner

devRael1 commented Apr 7, 2026

@copilot refait une analyse du code avec l'agent personnalisé "dev" et vérifie qu'il n'y a pas de manquements et que le code est professionnel, détaillé et documenté.

- Add missing TorrentsClient query string tests (WithAllParams, WithNullParams)
  for parity with Usenet/WebDownloads test coverage
- Add missing SearchNewznabAsync_WithApiKey_IncludesInQueryString test
  for parity with Torznab
- Fix test naming: CheckForInactiveAsync_WithNullAuthId → WithNullOptions
- Fix StreamExample: TORBOX_AUTH_TOKEN → TORBOX_API_KEY with clarifying comments
- Fix DownloadSearchResultsExample: improve GUID placeholder comment
- Fix QueuedDownloadsExample: document valid Type values
- Unit tests: 315 → 318 (all passing)

Agent-Logs-Url: https://github.com/devRael1/TorBoxSDK/sessions/ed0b3f65-4502-4755-969a-200bbf22d302

Co-authored-by: devRael1 <91017912+devRael1@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 7, 2026

@copilot refait une analyse du code avec l'agent personnalisé "dev" et vérifie qu'il n'y a pas de manquements et que le code est professionnel, détaillé et documenté.

Analyse complète effectuée avec 4 agents spécialisés en parallèle. Voici le résumé :

Agent code-reviewer (modèles) — 12 fichiers → APPROVED (5 NITPICK, 0 CRITICAL/MAJOR/MINOR)

  • Tous les sealed records, [JsonPropertyName], nullability, XML docs conformes

Agent code-reviewer (interfaces/implémentations) — 18 fichiers → APPROVED WITH MINOR ISSUES

  • Règle 3 catégories bien appliquée, ConfigureAwait(false) partout, validation cohérente
  • 3 MINOR : duplication code multipart TorrentsClient/UsenetClient + BuildSearchQuery identique dans SearchApiClient (DRY à adresser en follow-up, pré-existant avant cette PR)

Agent tests — 14 fichiers → APPROVED avec gaps de couverture identifiés et corrigés :

  • ✅ Ajouté GetMyTorrentListAsync_WithAllParams_IncludesInQueryString (parité Usenet/WebDl)
  • ✅ Ajouté GetMyTorrentListAsync_WithNullParams_OmitsFromQueryString (parité Usenet/WebDl)
  • ✅ Ajouté SearchNewznabAsync_WithApiKey_IncludesInQueryString (parité Torznab)
  • ✅ Corrigé nommage CheckForInactiveAsync_WithNullAuthIdWithNullOptions

Agent docs (exemples) — 13 fichiers → APPROVED avec corrections appliquées :

  • ✅ StreamExample: TORBOX_AUTH_TOKENTORBOX_API_KEY avec commentaires explicatifs
  • ✅ DownloadSearchResultsExample: commentaire GUID amélioré
  • ✅ QueuedDownloadsExample: types valides documentés

Résultat final : Build 0W/0E, 318 tests unitaires ✅, 42 tests intégration ✅. Commit d44c6bc.

Copilot AI requested a review from devRael1 April 7, 2026 13:14
@devRael1 devRael1 marked this pull request as ready for review April 7, 2026 15:48
Copilot AI review requested due to automatic review settings April 7, 2026 15:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR standardizes TorBoxSDK’s public method signatures around a consistent Options/Request pattern to reduce API-surface inconsistency and make parameter passing more predictable across clients.

Changes:

  • Replaced multi-parameter public methods with *Options / *Request models across several clients (Search, Relay, Torrents, Usenet, WebDownloads, Stream, Queued, Notifications, Integrations).
  • Added new Options models (and wired an existing ExportDataOptions) to represent query/path parameters.
  • Updated unit/integration tests and examples to use the new method signatures and added additional validation/query-string assertions.

Reviewed changes

Copilot reviewed 57 out of 57 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/TorboxSDK.UnitTests/Search/SearchApiClientTests.cs Updates tests to Options-based Search APIs; adds Newznab apikey test
tests/TorboxSDK.UnitTests/Relay/RelayApiClientTests.cs Updates Relay tests to CheckInactiveOptions and adds empty-auth-id validation test
tests/TorboxSDK.UnitTests/Main/WebDownloads/WebDownloadsClientTests.cs Updates WebDownloads tests to GetMyListOptions / CheckCachedOptions
tests/TorboxSDK.UnitTests/Main/Usenet/UsenetClientTests.cs Updates Usenet tests to GetMyListOptions / CheckCachedOptions
tests/TorboxSDK.UnitTests/Main/Torrents/TorrentsClientTests.cs Adds query-string parity tests and null-options validation tests
tests/TorboxSDK.UnitTests/Main/Stream/StreamClientTests.cs Migrates Stream tests to CreateStreamOptions / GetStreamDataOptions
tests/TorboxSDK.UnitTests/Main/Queued/QueuedClientTests.cs Migrates queued list test to GetQueuedOptions
tests/TorboxSDK.UnitTests/Main/Notifications/NotificationsClientTests.cs Migrates Intercom hash test to GetIntercomHashOptions and adds validations
tests/TorboxSDK.UnitTests/Main/Integrations/IntegrationsClientTests.cs Updates OAuth register to provider-on-request and adds empty-provider validations
tests/TorBoxSDK.IntegrationTests/Search/SearchApiClientIntegrationTests.cs Updates Search integration tests to Options-based calls
tests/TorBoxSDK.IntegrationTests/Main/WebDownloads/WebDownloadsClientIntegrationTests.cs Updates WebDownloads integration test to CheckCachedOptions
tests/TorBoxSDK.IntegrationTests/Main/Usenet/UsenetClientIntegrationTests.cs Updates Usenet integration test to CheckCachedOptions
tests/TorBoxSDK.IntegrationTests/Main/Torrents/TorrentsClientIntegrationTests.cs Updates Torrents integration tests to CheckCachedOptions / GetTorrentInfoOptions
tests/TorBoxSDK.IntegrationTests/Main/Notifications/NotificationsClientIntegrationTests.cs Updates Notifications integration test to GetIntercomHashOptions
src/TorBoxSDK/Search/SearchApiClient.cs Updates Search endpoints to accept Options objects and validates options
src/TorBoxSDK/Search/ISearchApiClient.cs Updates interface signatures/docs to Options-based overloads
src/TorBoxSDK/Relay/RelayApiClient.cs Updates relay inactive check to CheckInactiveOptions
src/TorBoxSDK/Relay/IRelayApiClient.cs Updates relay interface/docs to Options-based inactive check
src/TorBoxSDK/Models/Torrents/GetTorrentInfoOptions.cs Adds new options model for torrentinfo query params
src/TorBoxSDK/Models/Stream/GetStreamDataOptions.cs Adds new options model for stream data query params
src/TorBoxSDK/Models/Stream/CreateStreamOptions.cs Adds new options model for stream creation query params
src/TorBoxSDK/Models/Search/SearchTorznabOptions.cs Adds Torznab search query options model
src/TorBoxSDK/Models/Search/SearchNewznabOptions.cs Adds Newznab search query options model
src/TorBoxSDK/Models/Search/DownloadUsenetOptions.cs Adds Usenet download path-parameter options model
src/TorBoxSDK/Models/Relay/CheckInactiveOptions.cs Adds relay inactive-check path-parameter options model
src/TorBoxSDK/Models/Queued/GetQueuedOptions.cs Adds queued downloads list query options model
src/TorBoxSDK/Models/Notifications/GetIntercomHashOptions.cs Adds Intercom hash query options model
src/TorBoxSDK/Models/Integrations/OAuthRegisterRequest.cs Moves provider to request with [JsonIgnore]
src/TorBoxSDK/Models/Common/GetMyListOptions.cs Adds shared list query options model
src/TorBoxSDK/Models/Common/CheckCachedOptions.cs Adds shared cache-check query options model
src/TorBoxSDK/Main/WebDownloads/WebDownloadsClient.cs Migrates list + cache-check to Options models
src/TorBoxSDK/Main/WebDownloads/IWebDownloadsClient.cs Updates interface/docs for Options-based list + cache-check
src/TorBoxSDK/Main/Usenet/UsenetClient.cs Migrates list + cache-check to Options models
src/TorBoxSDK/Main/Usenet/IUsenetClient.cs Updates interface/docs for Options-based list + cache-check
src/TorBoxSDK/Main/Torrents/TorrentsClient.cs Migrates list/cache-check/export/torrentinfo to Options models
src/TorBoxSDK/Main/Torrents/ITorrentsClient.cs Updates interface/docs for Options-based list/cache/export/torrentinfo
src/TorBoxSDK/Main/Stream/StreamClient.cs Migrates stream create/data to Options models
src/TorBoxSDK/Main/Stream/IStreamClient.cs Updates interface/docs for Options-based stream calls
src/TorBoxSDK/Main/Queued/QueuedClient.cs Migrates getqueued to GetQueuedOptions
src/TorBoxSDK/Main/Queued/IQueuedClient.cs Updates interface/docs for Options-based queued listing
src/TorBoxSDK/Main/Notifications/NotificationsClient.cs Migrates Intercom hash call to Options model
src/TorBoxSDK/Main/Notifications/INotificationsClient.cs Updates interface/docs for Options-based Intercom hash
src/TorBoxSDK/Main/Integrations/IntegrationsClient.cs Migrates OAuth register to provider-on-request
src/TorBoxSDK/Main/Integrations/IIntegrationsClient.cs Updates interface/docs for Options/Request-based OAuth register
src/TorBoxSDK.Examples/Search/SearchMetaExample.cs Updates Search examples to Options-based calls
src/TorBoxSDK.Examples/Search/DownloadSearchResultsExample.cs Updates Usenet download example to DownloadUsenetOptions
src/TorBoxSDK.Examples/Relay/RelayExample.cs Updates relay example to CheckInactiveOptions
src/TorBoxSDK.Examples/Main/WebDownloads/WebDownloadsAdvancedExample.cs Updates webdl cache-check example to CheckCachedOptions
src/TorBoxSDK.Examples/Main/Usenet/UsenetAdvancedExample.cs Updates usenet cache-check example to CheckCachedOptions
src/TorBoxSDK.Examples/Main/Usenet/ListUsenetExample.cs Updates usenet list example to GetMyListOptions
src/TorBoxSDK.Examples/Main/Torrents/ListTorrentsExample.cs Updates torrent list example to GetMyListOptions
src/TorBoxSDK.Examples/Main/Torrents/DownloadTorrentExample.cs Updates export example to ExportDataOptions
src/TorBoxSDK.Examples/Main/Torrents/CheckCachedExample.cs Updates cache-check + torrentinfo examples to Options models
src/TorBoxSDK.Examples/Main/Stream/StreamExample.cs Updates stream example to Options models + env var clarification
src/TorBoxSDK.Examples/Main/Queued/QueuedDownloadsExample.cs Updates queued filtering/single fetch to GetQueuedOptions
src/TorBoxSDK.Examples/Main/Notifications/NotificationsExample.cs Updates Intercom hash example to GetIntercomHashOptions
src/TorBoxSDK.Examples/Main/Integrations/OAuthExample.cs Updates OAuth register example to provider-on-request

public async Task<TorBoxResponse<object>> CheckCachedAsync(CheckCachedOptions options, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(hashes);
ArgumentNullException.ThrowIfNull(options);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

CheckCachedAsync validates options but not options.Hashes. If a caller sets Hashes = null (possible via null-forgiving), string.Join will throw ArgumentNullException with a non-actionable param name ("values") and inconsistent behavior vs the previous hashes argument validation. Consider explicitly validating options.Hashes (and ideally throwing with paramName matching the property/argument) before calling string.Join.

Suggested change
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(options.Hashes, $"{nameof(options)}.{nameof(CheckCachedOptions.Hashes)}");

Copilot uses AI. Check for mistakes.
public async Task<TorBoxResponse<object>> CheckCachedAsync(CheckCachedOptions options, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(hashes);
ArgumentNullException.ThrowIfNull(options);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

CheckCachedAsync checks options for null, but options.Hashes can still be null (e.g., new CheckCachedOptions { Hashes = null! }), which will throw from string.Join with a generic param name. Add an explicit null check for options.Hashes so the SDK throws a clear/consistent ArgumentNullException tied to the public API surface.

Suggested change
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(options.Hashes);

Copilot uses AI. Check for mistakes.
public async Task<TorBoxResponse<object>> CheckCachedAsync(CheckCachedOptions options, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(hashes);
ArgumentNullException.ThrowIfNull(options);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

Same issue as other clients: CheckCachedAsync validates options but not options.Hashes. If Hashes is null, string.Join throws with an internal param name. Add an explicit null check for options.Hashes (and consider keeping exception semantics consistent with the old hashes parameter).

Suggested change
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(options.Hashes, nameof(options.Hashes));

Copilot uses AI. Check for mistakes.
Comment on lines 64 to +114
@@ -97,22 +96,20 @@ public interface ISearchApiClient
Task<TorBoxResponse<MetaSearchResult>> GetMetaByIdAsync(string id, CancellationToken cancellationToken = default);

/// <summary>Searches the Torznab API for torrents matching the specified query.</summary>
/// <param name="query">The search query string.</param>
/// <param name="apiKey">Optional API key override for the Torznab endpoint.</param>
/// <param name="options">The search options containing the query and optional API key.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The search results as an XML string.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="query"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="query"/> is empty.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the query in <paramref name="options"/> is empty.</exception>
/// <exception cref="TorBoxException">Thrown when the API returns an error.</exception>
Task<TorBoxResponse<string>> SearchTorznabAsync(string query, string? apiKey = null, CancellationToken cancellationToken = default);
Task<TorBoxResponse<string>> SearchTorznabAsync(SearchTorznabOptions options, CancellationToken cancellationToken = default);

/// <summary>Searches the Newznab API for usenet articles matching the specified query.</summary>
/// <param name="query">The search query string.</param>
/// <param name="apiKey">Optional API key override for the Newznab endpoint.</param>
/// <param name="options">The search options containing the query and optional API key.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The search results as an XML string.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="query"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="query"/> is empty.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the query in <paramref name="options"/> is empty.</exception>
/// <exception cref="TorBoxException">Thrown when the API returns an error.</exception>
Task<TorBoxResponse<string>> SearchNewznabAsync(string query, string? apiKey = null, CancellationToken cancellationToken = default);
Task<TorBoxResponse<string>> SearchNewznabAsync(SearchNewznabOptions options, CancellationToken cancellationToken = default);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The XML docs for the Options-based methods don’t fully match the actual thrown exceptions. For example, DownloadUsenetAsync/SearchTorznabAsync/SearchNewznabAsync will also throw ArgumentNullException when options.Id/options.Guid/options.Query are null (via Guard.ThrowIfNullOrEmpty), but the docs only describe ArgumentException for empty values. Update the <exception> docs (or adjust validation) so consumers can rely on the documented exception behavior.

Copilot uses AI. Check for mistakes.
Comment on lines 17 to +24
/// <summary>Checks whether a torrent is inactive on the relay.</summary>
/// <param name="authId">The authentication identifier of the user.</param>
/// <param name="torrentId">The unique identifier of the torrent to check.</param>
/// <param name="options">The options containing the auth ID and torrent ID.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The inactivity check result.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="authId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="authId"/> is empty.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the auth ID in <paramref name="options"/> is empty.</exception>
/// <exception cref="TorBoxException">Thrown when the API returns an error.</exception>
Task<TorBoxResponse<InactiveCheckResult>> CheckForInactiveAsync(string authId, long torrentId, CancellationToken cancellationToken = default);
Task<TorBoxResponse<InactiveCheckResult>> CheckForInactiveAsync(CheckInactiveOptions options, CancellationToken cancellationToken = default);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

CheckForInactiveAsync documentation says ArgumentException is thrown when the auth ID in options is empty, but the implementation uses Guard.ThrowIfNullOrEmpty and will throw ArgumentNullException if options.AuthId is null. Update the <exception> docs to reflect both null and empty auth-id cases (or change validation to match the docs).

Copilot uses AI. Check for mistakes.
Comment on lines 11 to +27
/// <summary>Creates a stream for a download item.</summary>
/// <param name="id">The identifier of the download.</param>
/// <param name="fileId">The identifier of the file within the download.</param>
/// <param name="type">The type of download (e.g., torrent, usenet).</param>
/// <param name="chosenSubtitleIndex">Optional index of the chosen subtitle track.</param>
/// <param name="chosenAudioIndex">Optional index of the chosen audio track.</param>
/// <param name="chosenResolutionIndex">Optional index of the chosen resolution.</param>
/// <param name="options">The stream creation options containing the item ID, file ID, type, and optional track indices.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The stream URL or data as a string.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="type"/> is empty.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the type in <paramref name="options"/> is empty.</exception>
/// <exception cref="TorBoxException">Thrown when the API returns an error.</exception>
Task<TorBoxResponse<string>> CreateStreamAsync(long id, long fileId, string type, int? chosenSubtitleIndex = null, int? chosenAudioIndex = null, int? chosenResolutionIndex = null, CancellationToken cancellationToken = default);
Task<TorBoxResponse<string>> CreateStreamAsync(CreateStreamOptions options, CancellationToken cancellationToken = default);

/// <summary>Gets stream data for a download item.</summary>
/// <param name="presignedToken">The presigned token for stream authentication.</param>
/// <param name="token">The authentication token.</param>
/// <param name="chosenSubtitleIndex">Optional index of the chosen subtitle track.</param>
/// <param name="chosenAudioIndex">Optional index of the chosen audio track.</param>
/// <param name="chosenResolutionIndex">Optional index of the chosen resolution.</param>
/// <param name="options">The stream data options containing the presigned token, token, and optional track indices.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The stream data.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="presignedToken"/> or <paramref name="token"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="presignedToken"/> or <paramref name="token"/> is empty.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the presigned token or token in <paramref name="options"/> is empty.</exception>
/// <exception cref="TorBoxException">Thrown when the API returns an error.</exception>
Task<TorBoxResponse<object>> GetStreamDataAsync(string presignedToken, string token, int? chosenSubtitleIndex = null, int? chosenAudioIndex = null, int? chosenResolutionIndex = null, CancellationToken cancellationToken = default);
Task<TorBoxResponse<object>> GetStreamDataAsync(GetStreamDataOptions options, CancellationToken cancellationToken = default);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The docs for CreateStreamAsync/GetStreamDataAsync indicate ArgumentException for empty Type/tokens, but the implementation uses Guard.ThrowIfNullOrEmpty and will throw ArgumentNullException if those option properties are null. Adjust the <exception> docs (or the validation) to keep the public contract accurate.

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +47
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the auth ID or email in <paramref name="options"/> is empty.</exception>
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

GetIntercomHashAsync uses Guard.ThrowIfNullOrEmpty on options.AuthId and options.Email, so it can throw ArgumentNullException if either property is null. The XML docs currently only mention ArgumentException for empty values; please update the docs (or validation) so null vs empty behavior is accurately documented.

Suggested change
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the auth ID or email in <paramref name="options"/> is empty.</exception>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="options"/> is <see langword="null"/>, or when
/// <paramref name="options"/>.<see cref="GetIntercomHashOptions.AuthId"/> or
/// <paramref name="options"/>.<see cref="GetIntercomHashOptions.Email"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="options"/>.<see cref="GetIntercomHashOptions.AuthId"/> or
/// <paramref name="options"/>.<see cref="GetIntercomHashOptions.Email"/> is empty.
/// </exception>

Copilot uses AI. Check for mistakes.
/// <returns>The API response.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="provider"/> is <see langword="null"/> or <paramref name="request"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="provider"/> is empty.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

OAuthRegisterAsync now takes only request, and the implementation validates request.Provider via Guard.ThrowIfNullOrEmpty, which can throw ArgumentNullException if Provider is null. The docs only mention ArgumentException for an empty provider. Update the <exception> docs to reflect the actual null/empty behavior of request.Provider (or alter validation to match the docs).

Suggested change
/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>, or when the provider in <paramref name="request"/> is <see langword="null"/>.</exception>

Copilot uses AI. Check for mistakes.
@devRael1
Copy link
Copy Markdown
Owner

devRael1 commented Apr 7, 2026

@copilot apply changes based on the comments in this thread

…xception docs

- Add explicit ArgumentNullException.ThrowIfNull(options.Hashes) in CheckCachedAsync
  across TorrentsClient, UsenetClient, WebDownloadsClient (prevents generic string.Join
  exception with non-actionable param name)
- Update XML docs on 8 interfaces to document both ArgumentNullException (null) and
  ArgumentException (empty) for inner option properties validated via Guard.ThrowIfNullOrEmpty:
  ISearchApiClient (DownloadUsenet, SearchTorznab, SearchNewznab),
  IRelayApiClient (CheckForInactive), IStreamClient (CreateStream, GetStreamData),
  INotificationsClient (GetIntercomHash), IIntegrationsClient (OAuthRegister)
- Update XML docs on ITorrentsClient, IUsenetClient, IWebDownloadsClient to document
  ArgumentNullException for null Hashes collection in CheckCachedAsync
- Fix test naming: CheckCachedAsync_WithNullHashes → WithNullOptions for null-options tests
- Add 3 new tests for null-Hashes property validation (1 per client)
- Unit tests: 318 → 321 (all passing), Build: 0W/0E

Agent-Logs-Url: https://github.com/devRael1/TorBoxSDK/sessions/e49df0a9-9d88-40ff-b1ec-04428bed644e

Co-authored-by: devRael1 <91017912+devRael1@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 7, 2026

@copilot apply changes based on the comments in this thread

All 8 review comments applied in commit ae2d044:

Validation fixes (3 clients):

  • Added explicit ArgumentNullException.ThrowIfNull(options.Hashes) in CheckCachedAsync across TorrentsClient, UsenetClient, WebDownloadsClient — prevents string.Join from throwing with a generic "values" param name

XML docs fixes (8 interfaces):

  • ISearchApiClientDownloadUsenetAsync, SearchTorznabAsync, SearchNewznabAsync
  • IRelayApiClientCheckForInactiveAsync
  • IStreamClientCreateStreamAsync, GetStreamDataAsync
  • INotificationsClientGetIntercomHashAsync
  • IIntegrationsClientOAuthRegisterAsync
  • ITorrentsClient, IUsenetClient, IWebDownloadsClientCheckCachedAsync

All now document both ArgumentNullException (null) and ArgumentException (empty) for inner properties validated via Guard.ThrowIfNullOrEmpty.

Tests: 3 new tests for null-Hashes + renamed null-options tests for naming accuracy. 321 tests pass, build 0W/0E.

@devRael1 devRael1 merged commit 4f6001e into master Apr 7, 2026
@devRael1 devRael1 deleted the copilot/refactor-sdk-parameter-management branch April 7, 2026 19:29
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