V9.0.0 - Modernize CI workflow, update project structure, and enhance API coverage#224
Merged
Conversation
…or different branches
…t stack - Add global.json (SDK 10.0.100), Directory.Packages.props (CPM), enable nullable/deterministic build/doc generation in Directory.Build.props. - Retarget CompaniesHouse and CompaniesHouse.Extensions.Microsoft. DependencyInjection to net8.0;net9.0;net10.0; drop netstandard/net45 and reference-assembly packages. - Retarget all test projects + sample to net10.0. - Convert CompaniesHouse.sln to CompaniesHouse.slnx; delete the old .sln. - Update Dockerfile (SDK 10.0, slnx) and CI workflow (checkout@v4 with recursive submodules, setup-dotnet@v4 via global.json, softprops/action-gh- release). - Replace NUnit + FluentAssertions with xUnit + Shouldly across all four test projects (FluentAssertions license changed to a paid tier from v8). Add a dependency-free EquivalencyAssertionExtensions helper in CompaniesHouse.Tests to replace the old FluentAssertions IEquivalencyStep/MapProviders machinery for bridging test-fixture wire strings against string-backed/enum response properties. - Add .plans/ (outstanding + completed) and AGENTS.md; move plan 00 to completed with full notes on the test-stack migration. - 594/597 tests pass; 3 pre-existing IntegrationTests failures are due to live Companies House API behaviour drift (200 + empty results instead of 404 for malformed company numbers), unrelated to this change. Co-authored-by: Copilot <[email protected]>
…ine, remove Newtonsoft) - Redesign CompaniesHouseClientResponse<T> with status code, reason phrase, retry-after, headers, IsSuccess - Add shared send/deserialize pipeline (ToCompaniesHouseClientResponseAsync); throw CompaniesHouseApiException only for 5xx - Central STJ JsonSerializerOptions with snake_case naming, EnumMember-aware enum converter, enum-array-or-single converter, polymorphic SearchItem converter - Remove Newtonsoft.Json entirely from src/CompaniesHouse (all response models, converters, csproj) - Update default base URI to api.company-information.service.gov.uk - Round out integration test coverage for insolvency info, registered office address, officer-by-appointment (valid + invalid real API cases)
…ed multi-client support - CompaniesHouseClientOptions/CompaniesHouseClientDocumentOptions now carry DataAnnotations validation (Required ApiKey/BaseUri). - Registration rebuilt around AddOptions<>().ValidateDataAnnotations().ValidateOnStart() so a missing/invalid API key fails fast at startup instead of on first request. - Added IConfiguration/IConfigurationSection binding overloads (default section CompaniesHouse / CompaniesHouseDocument). - Added named/keyed overloads (AddCompaniesHouseClient(name, ...)) so multiple distinct clients can coexist, resolved via GetRequiredKeyedService. - Exposed an optional Action<IHttpClientBuilder> hook on the flexible overloads for resilience handlers (Polly etc). - Bumped Microsoft.Extensions.* package versions and added Options/Configuration packages to Directory.Packages.props. - Expanded DI tests to cover config binding, keyed resolution and validation failure.
- CompanyStatus is now a readonly record struct wrapping the raw wire string, with a trivial [JsonConverter] applied directly on the type instead of the global enum converter, so unrecognised status values from Companies House round-trip instead of throwing. - No implicit string conversions; no None static member - default(CompanyStatus) / HasValue == false represents an absent value, per the plan's answered open questions. - IsKnown and Description (sourced from api-enumerations constants.yml) added. - CompanyProfile.CompanyStatus and search Company.CompanyStatus already used this type name, so no call-site changes were needed beyond one test's CompanyStatus.None -> default. - Added CompanyStatusTests covering known-value round-trip, unknown-value no-throw + raw preservation, null handling, equality, switch matching, and Description.
… workflow (plan 05) - Pin external/api-enumerations to upstream master. - enumerations/extra/ documents the submodule-first/extras-override merge rule for plan 04's generator, with a real example (closed-on company_status description missing upstream). - Dockerfile build stage now COPYs external/ and enumerations/ so the generator sees them inside the container build too. - New .github/workflows/bump-api-enumerations.yml fast-forwards the submodule monthly and opens a PR for review.
- New CompaniesHouse.SourceGenerator project (netstandard2.0, build-time-only analyzer referenced from CompaniesHouse.csproj, never shipped to consumers). - Hand-rolled MinimalYamlParser for the api-enumerations YAML subset, with dedicated unit tests. - MemberNameGenerator (wire-value -> PascalCase), EnumMapParser (enum-map.txt config format), and EnumDataMerger (submodule-first, extras-override merge). - ValueTypeEmitter reproduces the plan 03 CompanyStatus shape exactly (readonly record struct + JsonConverter, KnownValues, Descriptions, no member for an empty wire value). - CompanyStatus is now fully generator-produced from enum-map.txt; the hand-authored CompanyStatus.cs/CompanyStatusJsonConverter.cs are removed. Generated output includes ClosedOn from the enumerations/extra overlay, proving the submodule+extras+generator round trip end-to-end. - New CompaniesHouse.SourceGenerator.Tests project: 28 tests covering the parser, member-name conversion, merge rules, enum-map parsing, and end-to-end generator runs via CSharpGeneratorDriver with in-memory AdditionalTexts. - Full solution build (net8/9/10) 0 errors; CompaniesHouse.Tests 314/314; ScenarioTests 2/2; format clean on touched files.
Fix the company search restrictions query bug and complete the missing search surface with alphabetical, dissolved, and advanced company search endpoints. This adds dedicated request models, URI builders, typed response models, CompaniesHouseClient entry points, and DI registrations while keeping the existing search-client/factory architecture in place. Migrate CompanyType onto the enum source generator and add generated CompanySubtype support for advanced-search filters and responses. Expand unit, DI, source-generator, and integration coverage for the new builders, generated value types, and search deserialization paths.
Migrate CompanyStatusDetail and Jurisdiction from hand-written wire enums to the generated string-backed value-type pipeline, and add generated ForeignAccountType and TermsOfAccountPublication support from api-enumerations. Wire the new generated types into company profile deserialization and update the existing test enum mappings to the generated member names. Extend the company profile response models to cover the confirmed live API gaps: subtype, has_super_secure_pscs, external_registration_number, foreign_company_details, and the missing exemptions and uk_establishments links. Add focused unit, scenario, client, and integration coverage for the new fields and for the established 404 response semantics on GetCompanyProfileAsync.
The plan 07 commit (f75f7ab) marked several CompanyProfile/CompanyProfileLinks properties as C# 'required', but real Companies House API responses frequently omit them (e.g. annual_return, confirmation_statement, previous_company_names, sic_codes, branch_company_details, and most of the individual links - verified against live payloads for a standard company, an overseas company, a CIC, and a liquidation/dissolved company, none of which carry every link or every optional section). This broke STJ deserialization for the vast majority of real profiles with a JsonException about missing required properties. Reverted those properties to plain (non-required) members matching their pre-plan-07 declarations, and made the remaining CompanyProfileLinks members nullable since none of the observed payloads populate every link. Also fixed a test-only build break: the CompaniesHouseCompanyProfileClientTests private fields were marked 'required' with a private setter, which is invalid C# (a required member's setter cannot be less visible than the containing type) and failed the build with CS9032.
Cross-checked all 7 search endpoints against the live docs and real API responses. Fixed missing page_number on search/all, search/officers and search/disqualified-officers envelopes; added company search/search-all address_snippet and external_registration_number fields; changed company description_identifier to string[] and added matches.snippet/address_snippet handling; marked advanced-search and dissolved-search item fields optional where live payloads omit company_subtype, sic_codes, ordered_alpha_key_with_id, matched_previous_company_name or registered_office_address; and added live-captured scenario tests plus richer real-API integration coverage for restrictions, advanced filters, alphabetical paging, previous-name dissolved search and foreign company/officer payloads.
Migrate officers wire enums to generated string-backed value types by adding OfficerRole and IdentificationType to enum-map.txt and removing the hand-written OfficerRole enum. Extend the officers models with the confirmed live API fields for etags, person_number, is_pre_1992_appointment, identity_verification_details, list-level kind/links/items_per_page/inactive_count, officer links.self, and the restored OfficerId convenience derived from the appointments link. Also add the historic appointed_before field seen on real Tesco officer payloads. Update GetOfficersAsync and OfficersUriBuilder to default to a 35-item page size and support the documented register_type, register_view and order_by query parameters while omitting unsupplied optional values. Add unit, scenario, value-type, URI builder and live integration coverage for the confirmed Tesco and Informa officer payloads, including real appointment deserialization and corporate identification handling.
…iour The Companies House officers list endpoint returns 200 with an empty item list for a malformed/non-existent company number rather than a 404, so CompaniesHouseClientResponse.Data is populated (not null). Updated the pre-existing test to assert an empty result set instead of a null Data property, verified against the real live API.
…n 10) Adds IntegrationFactAttribute/IntegrationTheoryAttribute which skip cleanly (rather than fail) when COMPANIES_HOUSE_API_KEY is not set, applied to the search/company-profile/officer integration test suites (charges/filing- history/PSC/documents/registered-office/appointments left for the plan 09 implementation pass to avoid clashing with concurrent work there). Verified by running with and without the env var: 35/35 pass with a key, 30/30 skip cleanly without one. Also adds a full-source snapshot test (ValueTypeEmitterSnapshotTests) that asserts the entire generated value-type + JSON converter source text for a representative enum group, guarding against accidental whitespace/shape regressions in ValueTypeEmitter beyond the existing spot-check assertions.
Rebuild the registered office address, filing history, officer appointments, PSC list, charges, insolvency, and document endpoints against the live Companies House APIs. This updates the prerelease models to match observed payloads, removes legacy wire-enum assumptions that break on new values, and moves the affected contracts onto the generated string-backed value-type pattern where the API exposes stable enum-like strings. The work also promotes registered office address to a public sub-client, adds dedicated URI builders for appointments and insolvency, and expands document metadata handling for real filenames, timestamps, and large content lengths. Add the supporting generator inputs and serializer changes needed for the new value types, including extra YAML groups for filing, charge, insolvency, and PSC values plus converter support for single-string-or-array payloads backed by record-struct value types. Update the affected tests across unit, scenario, DI, source-generator, and integration suites with live-schema assertions for 09a-09g, and serialize the integration project's real API tests to avoid flaky parallel execution against Companies House.
…plan 10) Applies IntegrationFactAttribute/IntegrationTheoryAttribute (added in a previous commit) to the charges, filing history, insolvency, PSC, document, registered-office-address, and officer-appointments-list integration test suites, so the whole CompaniesHouse.IntegrationTests project now skips cleanly rather than failing when COMPANIES_HOUSE_API_KEY is not set. Verified: 73/73 pass with the key present (run three times, one transient run showed unrelated real-API rate-limiting/500s that cleared immediately on retry), and the full suite (58 non-parameterised Skip results - xUnit collapses Theory data rows to a single Skip entry) skips cleanly with the env var unset.
…num types Generated readonly record struct enum types previously duplicated each wire value as a string literal twice: once for the static member and again for the KnownValues hash set / Descriptions dictionary key. KnownValues and Descriptions now reference the static member's .Value property instead, so the raw wire string is defined once.
…aniesHouseResponse<T> - Add CompaniesHouseResponse<T> abstract class with Success, NotFound, RateLimited, Unauthorized, ClientError, and ServerError sealed subtypes - Delete CompaniesHouseClientResponse<T> flat class and CompaniesHouseApiException - Rewrite HttpResponseMessageExtensions to map HTTP status codes to subtypes; remove EnsureNotServerErrorAsync (5xx is now a ServerError subtype, not an exception) - Update all interfaces and client implementations to use the new type - Rewrite CompaniesHouseDocumentDownloadClient to use the discriminated union inline - Update unit tests: replace IsSuccess/Data-null assertions with type checks; rewrite HttpResponseMessageExtensionsTests for the new contract - Update integration tests: replace CompaniesHouseApiException catch with pattern match on ServerError subtype - Update SampleProject to pattern-match on Success for company profile display
- README: switch the full-branching example to a switch expression - MIGRATION.md: replace stale CompaniesHouseClientResponse<T> section with discriminated union before/after (including CompaniesHouseApiException removal) - SampleProject: proper non-success handling in all Display methods, switch expression for company profile, alias to resolve Officer ambiguity, drop redundant System.* usings
All 'Invalid' tests were asserting .Data.ShouldBeNull() on 404 responses, which now throws InvalidOperationException. Updated to assert NotFound subtype using ShouldBeOfType<CompaniesHouseResponse<T>.NotFound>() instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces several infrastructure and documentation improvements to the repository, focusing on modernizing the build system, centralizing dependency management, and enhancing CI/CD automation.
Infrastructure and CI/CD Enhancements:
Modernized the GitHub Actions CI workflow (
.github/workflows/continuous-integration-workflow.yml):actions/checkout@v4,actions/setup-dotnet@v4) and enabled recursive submodule checkout.masterandprereleasebranches, and automated release notes generation with NuGet package links.README.mdand correct nuspec metadata.Added a scheduled workflow to automatically bump the
api-enumerationssubmodule monthly and open a pull request if updates are detected. (.github/workflows/bump-api-enumerations.yml)Registered the
api-enumerationssubmodule in.gitmodulesto enable its use in the repository.Summary of Most Important Changes:
CI/CD and Build System Modernization
api-enumerationssubmodule and create pull requests for changes.api-enumerationssubmodule in.gitmodulesfor future use.