Skip to content

feat: API extensibility — protected surface + archived filter (closes #4)#7

Merged
diomonogatari merged 7 commits into
mainfrom
feat/issue-4-extensibility
Jun 5, 2026
Merged

feat: API extensibility — protected surface + archived filter (closes #4)#7
diomonogatari merged 7 commits into
mainfrom
feat/issue-4-extensibility

Conversation

@diomonogatari
Copy link
Copy Markdown
Owner

@diomonogatari diomonogatari commented Jun 5, 2026

Summary

Makes BitbucketClient extensible without forking — the actionable idea from #4 — plus a couple of adjacent, repo-first improvements. Decisions were made for the health of the library (and its consumer, stash-mcp), favouring a clean, documented-only API over literal satisfaction of the original request.

Closes #4.

What changed

Area Change
Extensibility The low-level building blocks are now protected instead of private: the paging family (GetPagedAsync/GetPagedResultsAsync + streams), response handlers (HandleResponseAsync/HandleErrorsAsync/ReadResponse*), JSON content builders, GetBaseUrl, and all 50 per-feature Get*Url() helpers. A subclass can now implement custom endpoints by reusing these primitives. 0 → 65 protected members; no public signature changed; nothing leaked onto an interface.
Archived filter New RepositoryArchivedState { Active, Archived, All } enum (mapped to the documented ACTIVE/ARCHIVED/ALL) and additive GetRepositoriesAsync / GetRepositoriesStreamAsync overloads on ISearchOperations that filter the /repos search endpoint. archived is a required lead argument, so existing all-optional signatures are untouched and resolution is unambiguous.
Parity fix GetPullRequestCommentLikesAsync gained avatarSize, matching its sibling GetCommitCommentLikesAsync.
CI fix Test project switched from the WireMock.Net meta-package to WireMock.Net.Minimal, removing the bundled WireMock.Net.OpenTelemetry → OpenTelemetry 1.14.0 transitive tree that tripped the NuGet audit (NU1902) under TreatWarningsAsErrors and broke restore/build. Same core test APIs; the OpenTelemetry feature was never used.
Docs CHANGELOG [Unreleased] section; README "Extending the client" + "Filtering archived repositories".

Decisions (repo-first)

  • archived lives on /repos search, not GetProjectRepositoriesAsync. The official REST descriptor documents the archived filter on the repository-search endpoint, not the per-project listing — so it's surfaced where the API actually supports it.
  • Three catalogued "param gaps" were intentionally dropped (deleted on users, from/to on audit, fromType on the PR list). None could be confirmed in the official Bitbucket REST API — same situation as archived. Shipping silently-ignored parameters would muddy the surface with version-dependent quirks, so they were left out. avatarSize stayed because it's the documented, sibling-established parameter.
  • Why protected, not public: keeps a clean subclassing path without freezing internals into a forever-supported public contract.

Testing

  • ExtensibilityMockTests — a subclass builds a custom paged endpoint purely from the protected helpers (single- and multi-page).
  • RepositorySearchMockTestsACTIVE/ARCHIVED/ALL are transmitted; the base overload omits archived.
  • An avatarSize transmission test on GetPullRequestCommentLikesAsync.
  • Full suite: 757 passing (749 baseline + 8), Release build clean under TreatWarningsAsErrors.

Flip the low-level primitives (paging family, response handling, JSON
content builders, GetBaseUrl) from private to protected so consumers can
subclass BitbucketClient and implement custom endpoints without forking —
the primary extensibility ask in #4. Static helpers stay static.

Adds ExtensibilityMockTests proving a subclass can build a custom paged
endpoint purely from the protected surface (single- and multi-page).

Refs #4
Flip all 50 Get*Url() request builders (GetReposUrl, GetAdminUrl,
GetProjectsUrl, GetPatUrl, ...) from private to protected so subclasses can
compose feature endpoints without reimplementing URL construction. Pairs
with the protected GetBaseUrl exposed in the previous commit.

Refs #4
Add a RepositoryArchivedState { Active, Archived, All } enum (mapped to the
documented ACTIVE/ARCHIVED/ALL query values) and overloads of
GetRepositoriesAsync / GetRepositoriesStreamAsync on ISearchOperations that
filter the /repos search endpoint by archived state. The new overload takes
archived as a required lead argument, so it is additive and never ambiguous
against the existing all-optional signature. Shared query construction is
factored into BuildRepositoriesQuery.

Per the official REST descriptor, archived filtering lives on /repos (not on
the per-project repos listing), so it is surfaced there.

Refs #4
…Async

Add the avatarSize query parameter to GetPullRequestCommentLikesAsync so it
matches its sibling GetCommitCommentLikesAsync (both return users). Modeled
inline as a trailing optional to mirror the sibling exactly; an overload was
unsuitable because an int avatarSize collides with the existing int? paging
optionals and would silently rebind positional callers.

Refs #4
Add a CHANGELOG [Unreleased] section and README sections ('Extending the
client', 'Filtering archived repositories') covering the #4 work.

Refs #4
The WireMock.Net meta-package bundles WireMock.Net.OpenTelemetry, which drags
in OpenTelemetry 1.14.0 transitively. Those packages carry moderate-severity
advisories, so the NuGet vulnerability audit raises NU1902 and
TreatWarningsAsErrors promotes it to an error — breaking restore/build of the
test project (and thus CI) even though the code is unrelated.

Switch to WireMock.Net.Minimal (same version), which exposes the identical core
APIs the tests use (WireMockServer, Request/Response builders, matchers) without
the optional OpenTelemetry/GraphQL/CSScript bundles. Removes the vulnerable
transitive tree entirely rather than suppressing the audit.
Apply dotnet format whitespace/style to the files added in this branch so they
satisfy the repo's verify-format CI gate (CRLF + single final newline per
.editorconfig).
@diomonogatari diomonogatari merged commit d3e1a9b into main Jun 5, 2026
1 check passed
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.

Improve API extensibility (expose protected/public methods)

1 participant