Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6d5cb19
refactor: remove reflection fallback from JSON deserialization
diomonogatari Feb 11, 2026
9995837
perf: deserialize JSON responses directly from stream
diomonogatari Feb 11, 2026
1e30035
perf: migrate enum lookup dictionaries to FrozenDictionary
diomonogatari Feb 11, 2026
ce74b02
feat: implement IDisposable on BitbucketClient
diomonogatari Feb 11, 2026
4b88cf0
feat: add ExecuteAsync methods and architectural error-handling test
diomonogatari Feb 11, 2026
78e55fa
refactor!: return IReadOnlyList<T> instead of IEnumerable<T>
diomonogatari Feb 11, 2026
7f618ed
feat: add input validation guards on public API methods
diomonogatari Feb 11, 2026
caf157a
docs: add P1+P2 changes to changelog for 1.0.0 release
diomonogatari Feb 11, 2026
ecef7a6
feat: freeze JsonSerializerOptions explicitly via MakeReadOnly()
diomonogatari Feb 11, 2026
1d9341f
refactor: consolidate enum mappings into generic EnumMap<TEnum>
diomonogatari Feb 11, 2026
e38a2a5
test: add ConfigureAwait(false) verification architectural test
diomonogatari Feb 11, 2026
f926433
refactor: convert 377 response model properties to init-only
diomonogatari Feb 11, 2026
656f58d
feat: introduce 13 dedicated request DTOs for write operations
diomonogatari Feb 11, 2026
b37846a
feat: add fluent query builders for complex endpoints
diomonogatari Feb 11, 2026
8887bcc
perf: add Utf8JsonReader-based PagedResultsReader for hot paths
diomonogatari Feb 11, 2026
fb46d79
feat: expose rate-limit headers on BitbucketRateLimitException
diomonogatari Feb 11, 2026
2f3cb03
chore: improve NuGet package metadata and bump to 1.0.0
diomonogatari Feb 11, 2026
678e636
docs: update DI examples to Polly v8 ResiliencePipeline syntax
diomonogatari Feb 12, 2026
39d81cd
chore: whitespace cleanup on readme.md
diomonogatari Feb 12, 2026
e4adeab
feat: extract IBitbucketClient interface for DI and testability
diomonogatari Feb 12, 2026
537dff6
test: remove useless InterfaceTests
diomonogatari Feb 12, 2026
2459706
feat: add OpenTelemetry tracing via ActivitySource
diomonogatari Feb 12, 2026
5925a1f
refactor: remove unused extension classes
diomonogatari Feb 12, 2026
ab63809
refactor: remove uncalled conversion methods from BitbucketHelpers
diomonogatari Feb 12, 2026
8b866c9
refactor: remove unused ExecuteAsync overloads
diomonogatari Feb 12, 2026
af88fbc
refactor: inline UnixDateTimeExtensions into converter
diomonogatari Feb 12, 2026
eaff4aa
refactor: add GetPagedAsync and GetPagedStreamAsync helpers
diomonogatari Feb 12, 2026
be5965d
refactor: migrate 82 paginated endpoints to shared helpers
diomonogatari Feb 12, 2026
b85d233
refactor: decompose IBitbucketClient into domain-specific sub-interfaces
diomonogatari Feb 12, 2026
d1e8a8f
docs: update README and CHANGELOG for 1.0.0 stable release
diomonogatari Feb 13, 2026
f193ba0
docs: correct date format for version 0.3.0 in CHANGELOG
diomonogatari Feb 13, 2026
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
150 changes: 149 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,155 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.0.0] - 2026-02-12

### Breaking Changes

- **`IReadOnlyList<T>` return types**: All buffered collection methods
now return `Task<IReadOnlyList<T>>` instead of `Task<IEnumerable<T>>`.
Consumers assigning results to `IEnumerable<T>` are unaffected;
consumers assigning to `List<T>` must add `.ToList()` or change the
variable type.
- **Init-only model properties**: 377 properties across 106
response model classes converted from `{ get; set; }` to
`{ get; init; }`. Models used as request bodies (32 files, 98
properties) retain mutable setters to allow consumer construction.
Consumers that assign model properties after construction must move
to object-initializer syntax.
- **Dedicated request DTOs**: Write operations now accept
purpose-built request DTOs instead of reusing response models or
inline parameters. 13 request DTOs created:
`CreateProjectRequest`, `UpdateProjectRequest`,
`CreateRepositoryRequest`, `ForkRepositoryRequest`,
`CreatePullRequestRequest`, `UpdatePullRequestRequest`,
`CreateBranchRequest`, `CreateTaskRequest`, `UpdateTaskRequest`,
`CreateWebHookRequest`, `UpdateWebHookRequest`,
`AssociateBuildStatusRequest`, `MergePullRequestRequest`.
Methods affected: `CreateProjectAsync`, `UpdateProjectAsync`,
`CreateProjectRepositoryAsync`, `CreateProjectRepositoryForkAsync`,
`CreatePullRequestAsync`, `UpdatePullRequestAsync`,
`MergePullRequestAsync`, `CreateBranchAsync`, `CreateTaskAsync`,
`UpdateTaskAsync`, `CreateProjectRepositoryWebHookAsync`,
`UpdateProjectRepositoryWebHookAsync`,
`AssociateBuildStatusWithCommitAsync`.
Request DTOs use `required` and `init`-only properties,
exposing only API-relevant fields.

### Changed

- **Source-gen-only deserialization**: Removed the
`JsonUnknownTypeHandling.JsonNode` reflection fallback from the
read-path `JsonSerializerOptions`. Deserialization now uses only
the source-generated `BitbucketJsonContext`, eliminating
reflection-based metadata and improving trim safety. A separate
`s_writeJsonOptions` retains a reflection fallback solely for
serializing anonymous types in request bodies.
- **Stream-based deserialization**: `ReadResponseContentAsync<T>`
now calls `JsonSerializer.DeserializeAsync` directly on the HTTP
response stream instead of reading the body into a `string` first.
Eliminates a full UTF-16 string copy per response.
- **FrozenDictionary enum lookups**: All 25 enum-to-string
mapping dictionaries in `BitbucketHelpers` converted from
`Dictionary<TEnum, string>` to `FrozenDictionary<TEnum, string>`.
Added reverse `FrozenDictionary<string, TEnum>` with
`StringComparer.OrdinalIgnoreCase` for O(1) string-to-enum lookups.
- **Consolidated enum mappings**: Introduced generic
`EnumMap<TEnum>` as the single source of truth for all 25
enum-to-string mappings. Replaced 13 individual converter
subclasses with a unified `BitbucketEnumConverterFactory`.
Slimmed `BitbucketHelpers.cs` from ~1,100 to ~490 lines.
Added public `ToApiString()` extension methods on all enum types.
- **Frozen `JsonSerializerOptions`**: Both `s_jsonOptions`
and `s_writeJsonOptions` are now explicitly frozen via
`MakeReadOnly()` at construction time, preventing accidental
mutation from any thread.
- **`IReadOnlyList<T>` return types**: All 27 buffered
collection methods now return `Task<IReadOnlyList<T>>` instead of
`Task<IEnumerable<T>>`, communicating immutability and preventing
multiple-enumeration bugs.
- **Paginated endpoint helpers**: Introduced shared `GetPagedAsync<T>`
and `GetPagedStreamAsync<T>` methods, replacing ~300 lines of
duplicated pagination logic across 82 endpoints.
- **Dead code removal**: Removed unused `UnixDateTimeExtensions`,
`DictionaryExtensions`, uncalled `BitbucketHelpers` conversion
methods, and redundant `ExecuteAsync` overloads.

### Added

- **NuGet package metadata improvements**: Version bumped to
1.0.0. Added `PackageIcon` (128×128 placeholder in `assets/icon.png`),
expanded `PackageTags` (`rest-api`, `api-client`, `atlassian`, `sdk`,
`dotnet`), and conditional icon inclusion with `Condition="Exists(…)"`.
`dotnet pack` now produces `BitbucketServer.Net.1.0.0.nupkg` with
README, icon, and full metadata.
- **Rate-limit headers on `BitbucketRateLimitException`**:
HTTP 429 exceptions now expose `RetryAfter`, `RateLimit`,
`RateLimitRemaining`, and `RateLimitReset` properties parsed from
standard rate-limit response headers. Gracefully returns `null` for
missing or unparseable headers.
- **`PagedResultsReader` zero-allocation metadata parser**:
Internal `Utf8JsonReader`-based parser that extracts pagination
metadata (`isLastPage`, `nextPageStart`, `start`, `limit`, `size`)
directly from UTF-8 bytes without deserializing the full payload.
Opt-in for hot-path streaming scenarios.
- **`IDisposable` on `BitbucketClient`**: The client now
implements `IDisposable` with ownership tracking. Clients created
via the `(string url, ...)` constructors own and dispose the
underlying `FlurlClient`. Clients created via `(IFlurlClient, ...)`
or `(HttpClient, ...)` do not dispose the injected client. All public
methods throw `ObjectDisposedException` after disposal.
- **`ExecuteAsync` centralised error handling**: New
`ExecuteAsync<TResult>`, `ExecuteAsync` (bool), and
`ExecuteWithNoContentAsync` methods that wrap HTTP call + response
handling in a single call, reducing boilerplate in API methods.
- **Input validation guards**: ~130 public methods now
validate URL-path string parameters (`projectKey`, `repositorySlug`,
`commitId`, `hookKey`, `userSlug`, etc.) with
`ArgumentException.ThrowIfNullOrWhiteSpace()` at method entry.
Prevents malformed URLs and confusing server-side errors.
- **Fluent query builders**: New `PullRequestQueryBuilder`,
`CommitQueryBuilder`, `BranchQueryBuilder`, and `ProjectQueryBuilder`
classes providing a fluent API for complex queries. Entry points:
`client.PullRequests(...)`, `client.Commits(...)`,
`client.Branches(...)`, `client.Projects()`. Each builder supports
`GetAsync()` for buffered results and `StreamAsync()` for
`IAsyncEnumerable<T>` streaming. Existing flat methods are unchanged.
- **OpenTelemetry tracing**: HTTP calls are traced via an internal
`ActivitySource` named `"Bitbucket.Net"`. Add it to your
`TracerProviderBuilder` to get per-request spans with method, URL,
and status code attributes.
- **`IBitbucketClient` interface**: Extracted from `BitbucketClient`
for dependency injection and unit testing. The interface is composed
of 12 domain-specific sub-interfaces (`IProjectOperations`,
`IRepositoryOperations`, `IPullRequestOperations`, etc.) so
consumers can depend on only the slice they need.
- **Code search**: `SearchCodeAsync` and `SearchCodeStreamAsync`
methods wrapping the Bitbucket Server code search REST API.

### Testing

- Added `SourceGenCoverageTests` validating all model types are
registered in `BitbucketJsonContext`.
- Added `BitbucketClientDisposeTests` (6 tests) covering disposal
semantics and ownership tracking.
- Added `FluentQueryBuilderMockTests` (12 tests) covering all four
query builders with default parameters, custom options, streaming,
and input validation.
- Added `ArchitecturalTests` verifying all HTTP calls have error
handlers (`HandleResponseAsync`, `ExecuteAsync`, or `StatusCode`),
that `JsonSerializerOptions` are explicitly frozen, and that every
`await` uses `ConfigureAwait(false)`.
- Added `InputValidationTests` (17 parameterized theories) covering
null/empty/whitespace rejection for key path-segment parameters.
- Total test count: 749.

## [0.3.0] - 2026-02-010

### Added

- **Code search**: `SearchCodeAsync` and `SearchCodeStreamAsync`
methods wrapping the Bitbucket Server code search REST API
(`/rest/search/latest/search`).

## [0.2.0] - 2026-02-08

Expand Down
Loading