Add support for generating a single client from multiple OpenAPI specifications#904
Conversation
…s (openApiPaths) Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #904 +/- ##
==========================================
- Coverage 93.33% 92.68% -0.66%
==========================================
Files 23 23
Lines 1396 2063 +667
==========================================
+ Hits 1303 1912 +609
- Misses 47 51 +4
- Partials 46 100 +54
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds the ability to generate a single Refit client by merging multiple OpenAPI specifications provided via a new openApiPaths setting, intended to simplify generation for versioned APIs (e.g., V1 + V2).
Changes:
- Introduces
OpenApiPathstoRefitGeneratorSettingsand routes generation through a multi-doc merge path when populated. - Implements OpenAPI document merging in
OpenApiDocumentFactory(paths, schemas/definitions, tags; first document wins on duplicates). - Updates CLI settings validation, unit tests, and README documentation to reflect the new configuration option.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Refitter/SettingsValidator.cs | Accepts openApiPaths in settings file and validates using the first entry when openApiPath is missing. |
| src/Refitter.Tests/SettingsValidatorTests.cs | Expands validator test coverage for openApiPaths scenarios and updates the required-setting error text. |
| src/Refitter.Tests/MultipleOpenApiPathsTests.cs | Adds new tests verifying merged generation behavior from multiple specs. |
| src/Refitter.Core/Settings/RefitGeneratorSettings.cs | Adds OpenApiPaths setting and documents precedence over OpenApiPath. |
| src/Refitter.Core/RefitGenerator.cs | Uses merged OpenAPI document creation when OpenApiPaths is populated. |
| src/Refitter.Core/OpenApiDocumentFactory.cs | Adds multi-path CreateAsync overload and merge logic for documents. |
| README.md | Documents openApiPaths usage and behavior in .refitter files. |
| if (document.Components?.Schemas != null) | ||
| { | ||
| foreach (var schema in document.Components.Schemas) | ||
| { | ||
| if (!baseDocument.Components.Schemas.ContainsKey(schema.Key)) | ||
| baseDocument.Components.Schemas[schema.Key] = schema.Value; | ||
| } | ||
| } | ||
|
|
||
| if (document.Definitions != null) | ||
| { | ||
| foreach (var definition in document.Definitions) | ||
| { | ||
| if (!baseDocument.Definitions.ContainsKey(definition.Key)) | ||
| baseDocument.Definitions[definition.Key] = definition.Value; | ||
| } | ||
| } |
There was a problem hiding this comment.
Merge() assumes baseDocument.Components/baseDocument.Definitions are non-null when later documents contain schemas/definitions. If the base document lacks these sections but a subsequent document has them, this will throw when accessing baseDocument.Components.Schemas or baseDocument.Definitions. Initialize the target collections (e.g., create Components/Schemas/Definitions when null) before merging.
There was a problem hiding this comment.
Fixed in commit 1f663ee. Added null-conditional access (baseDocument.Components?.Schemas != null) before merging schemas. Note: NSwag initializes Components as a read-only property (it cannot be assigned), so we guard with the null-conditional operator rather than re-initializing.
| [Test] | ||
| public async Task Can_Generate_Code_From_Multiple_Paths() | ||
| { | ||
| var (file1, file2) = await CreateTestSpecFiles(); | ||
| var settings = new RefitGeneratorSettings | ||
| { | ||
| OpenApiPaths = [file1, file2] | ||
| }; | ||
|
|
||
| var sut = await RefitGenerator.CreateAsync(settings); | ||
| var generatedCode = sut.Generate(); | ||
| generatedCode.Should().NotBeNullOrWhiteSpace(); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Generated_Code_Contains_Operations_From_Both_Specs() | ||
| { | ||
| var (file1, file2) = await CreateTestSpecFiles(); | ||
| var settings = new RefitGeneratorSettings | ||
| { | ||
| OpenApiPaths = [file1, file2] | ||
| }; | ||
|
|
||
| var sut = await RefitGenerator.CreateAsync(settings); | ||
| var generatedCode = sut.Generate(); | ||
|
|
||
| generatedCode.Should().Contain("ListPetsV1"); | ||
| generatedCode.Should().Contain("ListPetsV2"); | ||
| } |
There was a problem hiding this comment.
These tests validate merged output via string assertions, but they never compile the generated code. Given the generator is expected to produce buildable C# (and the repo commonly verifies this with BuildHelper.BuildCSharp(...)), please add at least one test that builds the code produced from multiple OpenApiPaths to catch merge regressions that still contain the expected substrings.
There was a problem hiding this comment.
Added Can_Build_Generated_Code_From_Multiple_Paths test that uses BuildHelper.BuildCSharp() in commit 1f663ee.
| "openApiPath": "/path/to/your/openAPI", // Required if openApiPaths is not specified | ||
| "openApiPaths": [ // Required if openApiPath is not specified. Multiple OpenAPI specs are merged into a single client | ||
| "/path/to/your/openAPI/v1", | ||
| "/path/to/your/openAPI/v2" | ||
| ], |
There was a problem hiding this comment.
The README example shows both openApiPath and openApiPaths in the same .refitter file. Since they are described as an either/or (and openApiPaths taking precedence), this example can mislead users into thinking both are required or used together. Consider showing two separate examples (single vs multiple) or explicitly stating that openApiPath is ignored when openApiPaths is provided.
There was a problem hiding this comment.
Fixed in commit 1f663ee. The README example block now shows openApiPath alone (single spec) and a separate example showing openApiPaths (multiple specs) with the description clarifying they are mutually exclusive.
| if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OpenApiPath) && | ||
| refitGeneratorSettings.OpenApiPaths.Length > 0) | ||
| { | ||
| settings.OpenApiPath = refitGeneratorSettings.OpenApiPaths[0]; | ||
| } | ||
| else | ||
| { | ||
| settings.OpenApiPath = refitGeneratorSettings.OpenApiPath; | ||
| } |
There was a problem hiding this comment.
OpenApiPaths is documented/implemented as taking precedence when non-empty, but this validator only uses the first OpenApiPaths entry when OpenApiPath is blank. If a settings file contains both (e.g., legacy openApiPath plus new openApiPaths), validation will use/validate the wrong spec and can fail even though generation will merge openApiPaths. Consider selecting the first OpenApiPaths entry whenever the array is non-empty (or explicitly error when both are provided).
There was a problem hiding this comment.
Fixed in commit 1f663ee. ValidateFilePath now uses OpenApiPaths is { Length: > 0 } to prefer OpenApiPaths whenever the array is non-empty, regardless of whether OpenApiPath is also set.
| if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OpenApiPath) && | ||
| refitGeneratorSettings.OpenApiPaths.Length == 0) | ||
| { |
There was a problem hiding this comment.
refitGeneratorSettings.OpenApiPaths.Length will throw if the settings file contains "openApiPaths": null (System.Text.Json will set the array property to null despite the default initializer). Please null-guard (OpenApiPaths?.Length ?? 0) so invalid/partial configs fail with a validation error instead of crashing.
There was a problem hiding this comment.
Fixed in commit 1f663ee with (refitGeneratorSettings.OpenApiPaths?.Length ?? 0) == 0.
|
|
||
| private static async Task<OpenApiDocument> GetOpenApiDocument(RefitGeneratorSettings settings) | ||
| { | ||
| if (settings.OpenApiPaths.Length > 0) |
There was a problem hiding this comment.
settings.OpenApiPaths.Length can throw if OpenApiPaths is deserialized as null (e.g., settings JSON has "openApiPaths": null). Prefer a null-safe check such as settings.OpenApiPaths is { Length: > 0 } (and treat null as empty).
| if (settings.OpenApiPaths.Length > 0) | |
| if (settings.OpenApiPaths is { Length: > 0 }) |
There was a problem hiding this comment.
Applied the suggested change in commit 1f663ee: if (settings.OpenApiPaths is { Length: > 0 }).
| public static async Task<OpenApiDocument> CreateAsync(IEnumerable<string> openApiPaths) | ||
| { | ||
| var paths = openApiPaths.ToArray(); | ||
| if (paths.Length == 1) | ||
| return await CreateAsync(paths[0]); | ||
|
|
||
| var documents = new OpenApiDocument[paths.Length]; | ||
| for (var i = 0; i < paths.Length; i++) | ||
| documents[i] = await CreateAsync(paths[i]); | ||
|
|
||
| return Merge(documents); | ||
| } |
There was a problem hiding this comment.
CreateAsync(IEnumerable<string> openApiPaths) doesn't handle an empty sequence: paths.Length == 0 leads to documents[0] in Merge() throwing. Add explicit argument validation (null/empty) and return a clear ArgumentException/ArgumentOutOfRangeException instead of an index error.
There was a problem hiding this comment.
Fixed in commit 1f663ee. CreateAsync(IEnumerable<string>) now throws ArgumentException for both null and empty inputs before proceeding. Also added a OpenApiDocumentFactory_Throws_For_Empty_Paths test to verify this.
|
@copilot apply changes based on the comments in this thread |
…ld test, README Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
Updated [refitter](https://github.com/christianhelle/refitter) from 1.7.1 to 2.0.0. <details> <summary>Release notes</summary> _Sourced from [refitter's releases](https://github.com/christianhelle/refitter/releases)._ ## 2.0.0 ## What's Changed * Fix numeric format with pattern quirk - infer type from format for all numeric types by @Copilot in christianhelle/refitter#869 * Read group documentation from document tags. by @DJ4ddi in christianhelle/refitter#887 * Fix null reference and XML escaping in XmlDocumentationGenerator by @Copilot in christianhelle/refitter#890 * Fix build workflow: add dotnet restore before dotnet msbuild in Prepare step by @Copilot in christianhelle/refitter#892 christianhelle/refitter#895 * Fix MSBuild workflow by @christianhelle in christianhelle/refitter#898 * Add support for generating a single client from multiple OpenAPI specifications by @Copilot in christianhelle/refitter#904 * Migrate from Microsoft.OpenApi.Readers 1.x to Microsoft.OpenApi 3.x by @vgmello in christianhelle/refitter#907 * Improve Smoke Tests execution time by @christianhelle in christianhelle/refitter#915 * Fix #580: Nullable strings marked correctly by @christianhelle in christianhelle/refitter#921 * Fix #672: MultipleInterfaces ByTag method naming scoped per-interface by @christianhelle in christianhelle/refitter#922 * Fix #635: Refactor source generator to use context.AddSource() by @christianhelle in christianhelle/refitter#923 * Add custom format mappings configuration by @christianhelle in christianhelle/refitter#927 * Fix multipart form-data parameter extraction by @christianhelle in christianhelle/refitter#928 christianhelle/refitter#911 christianhelle/refitter#902 * Fix smoke tests: --interface-only variant missing using directive for contract types by @Copilot in christianhelle/refitter#933 * Fix SonarCloud Code Quality Issues by @Copilot in christianhelle/refitter#932 * Add debug logging for source generator when searching for .refitter files by @codymullins in christianhelle/refitter#743 * Fix: Base type not generated for types using oneOf with discriminator by @Copilot in christianhelle/refitter#906 * Add option for Method Level Authorization header attribute by @Roflincopter in christianhelle/refitter#897 * Fix PR #897 review feedback and add comprehensive bearer auth tests by @christianhelle in christianhelle/refitter#936 * Fix numeric suffix added to interface method names in ByTag mode by @Copilot in christianhelle/refitter#914 * Improve OpenAPI parse + codegen throughput by removing avoidable allocations and repeated regex work by @Copilot in christianhelle/refitter#937 * Move [JsonConverter] from enum properties to enum types by @christianhelle in christianhelle/refitter#938 * Fix broken CLI tool help text by @christianhelle in christianhelle/refitter#940 * Improve code coverage to >90% by @christianhelle in christianhelle/refitter#941 * Microsoft.OpenApi v3.4 by @christianhelle in christianhelle/refitter#945 * Add Unicode support for XML doc comment generation by @christianhelle in christianhelle/refitter#948 * Add PropertyNamingPolicy support for JSON property naming by @christianhelle in christianhelle/refitter#969 christianhelle/refitter#966 * Fix recursive schema stack overflows by @christianhelle in christianhelle/refitter#971 * Made Header Parameters for Security Schemes safe to use as C# variable name by @smoerijf in christianhelle/refitter#977 * Enhance schema alias handling and property name generation by @christianhelle in christianhelle/refitter#996 * Verify alias handling and sanitize PascalCase properties by @christianhelle in christianhelle/refitter#997 * Harden .refitter settings deserialization and output path handling by @christianhelle in christianhelle/refitter#1000 * Special-case the default .refitter filename before deriving OutputFilename by @coderabbitai[bot] in christianhelle/refitter#1002 * [v2.0 audit] Fix pre-release regressions from #1057 by @christianhelle in christianhelle/refitter#1064 * Resolve high-severity audit findings from #1057 by @christianhelle in christianhelle/refitter#1067 * [v2.0 audit] Close remaining verified #1057 regressions by @christianhelle in christianhelle/refitter#1070 * Harden generation flows by @christianhelle in christianhelle/refitter#1071 * Breaking changes and Migration Guide for v2.0.0 by @christianhelle in christianhelle/refitter#1009 * Handle equivalent duplicate schemas in multi-spec merge by @christianhelle in christianhelle/refitter#1076 * Tighten warning handling across Refitter builds by @christianhelle in christianhelle/refitter#1079 * Fix default solution path and update .NET version in VS Code config by @christianhelle in christianhelle/refitter#1082 * Fix docker smoke tests by @christianhelle in christianhelle/refitter#1081 * Sanitize malformed schema type names without renaming clean schemas by @christianhelle in christianhelle/refitter#1085 ... (truncated) ## 1.7.3 ## What's Changed * Add support for systems running only .NET 10.0 (without .NET 8.0 or 9.0) in Refitter.MSBuild by @christianhelle in christianhelle/refitter#882 * Update to return HttpResponseMessage for file downloads by @frogcrush in christianhelle/refitter#877 ## New Contributors * @frogcrush made their first contribution in christianhelle/refitter#877 **Full Changelog**: christianhelle/refitter@1.7.2...1.7.3 ## 1.7.2 **Implemented enhancements:** - Improve Immutable Records ergonomics [\#844](christianhelle/refitter#844) - Omit certain operation headers and include all others [\#840](christianhelle/refitter#840) - Create .refitter settings file as part of output [\#859](christianhelle/refitter#859) by @[christianhelle - Fix integerType enum deserialization issue [\#855](christianhelle/refitter#855) ([christianhelle](https://github.com/christianhelle)) - support custom nswag template directory \#844 [\#854](christianhelle/refitter#854) by @kmc059000 - Fix missing method parameter XML code-documentation [\#850](christianhelle/refitter#850) ([christianhelle](https://github.com/christianhelle)) **Fixed bugs:** - integerType parsing in settings file fails [\#851](christianhelle/refitter#851) - CS1573 : Method parameter has no matching XML comment [\#846](christianhelle/refitter#846) **Merged pull requests:** - docs: add frogcrush as a contributor for code [\#878](christianhelle/refitter#878) ([allcontributors[bot]](https://github.com/apps/allcontributors)) - Migrate solution files from .sln to .slnx format [\#876](christianhelle/refitter#876) ([Copilot](https://github.com/apps/copilot-swe-agent)) - Fix issue with randomly failing tests to due parallel execution [\#872](christianhelle/refitter#872) ([christianhelle](https://github.com/christianhelle)) - chore\(deps\): update dependency tunit to 1.9.2 [\#863](christianhelle/refitter#863) ([renovate[bot]](https://github.com/apps/renovate)) - chore\(deps\): update dependency tunit to 1.7.7 [\#862](christianhelle/refitter#862) ([renovate[bot]](https://github.com/apps/renovate)) - Add unit tests for WriteRefitterSettingsFile functionality [\#860](christianhelle/refitter#860) ([Copilot](https://github.com/apps/copilot-swe-agent)) - chore\(deps\): update dependency ruby to v4 [\#858](christianhelle/refitter#858) ([renovate[bot]](https://github.com/apps/renovate)) - chore\(deps\): update dependency tunit to 1.6.28 [\#857](christianhelle/refitter#857) ([renovate[bot]](https://github.com/apps/renovate)) - docs: add 0x2badc0de as a contributor for bug [\#856](christianhelle/refitter#856) ([allcontributors[bot]](https://github.com/apps/allcontributors)) - chore\(deps\): update dependency swashbuckle.aspnetcore to 10.1.0 [\#853](christianhelle/refitter#853) ([renovate[bot]](https://github.com/apps/renovate)) - chore\(deps\): update dependency tunit to 1.6.0 [\#852](christianhelle/refitter#852) ([renovate[bot]](https://github.com/apps/renovate)) - docs: add lilinus as a contributor for code [\#849](christianhelle/refitter#849) ([allcontributors[bot]](https://github.com/apps/allcontributors)) - chore\(deps\): update dependency ruby to v3.4.8 [\#845](christianhelle/refitter#845) ([renovate[bot]](https://github.com/apps/renovate)) - chore\(deps\): update dependency tunit to 1.5.70 [\#837](christianhelle/refitter#837) ([renovate[bot]](https://github.com/apps/renovate)) **Full Changelog**: christianhelle/refitter@1.7.1...1.7.2 Commits viewable in [compare view](christianhelle/refitter@1.7.1...2.0.0). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updated [Refitter.MSBuild](https://github.com/christianhelle/refitter) from 1.7.3 to 2.0.0. <details> <summary>Release notes</summary> _Sourced from [Refitter.MSBuild's releases](https://github.com/christianhelle/refitter/releases)._ ## 2.0.0 ## What's Changed * Fix numeric format with pattern quirk - infer type from format for all numeric types by @Copilot in christianhelle/refitter#869 * Read group documentation from document tags. by @DJ4ddi in christianhelle/refitter#887 * Fix null reference and XML escaping in XmlDocumentationGenerator by @Copilot in christianhelle/refitter#890 * Fix build workflow: add dotnet restore before dotnet msbuild in Prepare step by @Copilot in christianhelle/refitter#892 christianhelle/refitter#895 * Fix MSBuild workflow by @christianhelle in christianhelle/refitter#898 * Add support for generating a single client from multiple OpenAPI specifications by @Copilot in christianhelle/refitter#904 * Migrate from Microsoft.OpenApi.Readers 1.x to Microsoft.OpenApi 3.x by @vgmello in christianhelle/refitter#907 * Improve Smoke Tests execution time by @christianhelle in christianhelle/refitter#915 * Fix #580: Nullable strings marked correctly by @christianhelle in christianhelle/refitter#921 * Fix #672: MultipleInterfaces ByTag method naming scoped per-interface by @christianhelle in christianhelle/refitter#922 * Fix #635: Refactor source generator to use context.AddSource() by @christianhelle in christianhelle/refitter#923 * Add custom format mappings configuration by @christianhelle in christianhelle/refitter#927 * Fix multipart form-data parameter extraction by @christianhelle in christianhelle/refitter#928 christianhelle/refitter#911 christianhelle/refitter#902 * Fix smoke tests: --interface-only variant missing using directive for contract types by @Copilot in christianhelle/refitter#933 * Fix SonarCloud Code Quality Issues by @Copilot in christianhelle/refitter#932 * Add debug logging for source generator when searching for .refitter files by @codymullins in christianhelle/refitter#743 * Fix: Base type not generated for types using oneOf with discriminator by @Copilot in christianhelle/refitter#906 * Add option for Method Level Authorization header attribute by @Roflincopter in christianhelle/refitter#897 * Fix PR #897 review feedback and add comprehensive bearer auth tests by @christianhelle in christianhelle/refitter#936 * Fix numeric suffix added to interface method names in ByTag mode by @Copilot in christianhelle/refitter#914 * Improve OpenAPI parse + codegen throughput by removing avoidable allocations and repeated regex work by @Copilot in christianhelle/refitter#937 * Move [JsonConverter] from enum properties to enum types by @christianhelle in christianhelle/refitter#938 * Fix broken CLI tool help text by @christianhelle in christianhelle/refitter#940 * Improve code coverage to >90% by @christianhelle in christianhelle/refitter#941 * Microsoft.OpenApi v3.4 by @christianhelle in christianhelle/refitter#945 * Add Unicode support for XML doc comment generation by @christianhelle in christianhelle/refitter#948 * Add PropertyNamingPolicy support for JSON property naming by @christianhelle in christianhelle/refitter#969 christianhelle/refitter#966 * Fix recursive schema stack overflows by @christianhelle in christianhelle/refitter#971 * Made Header Parameters for Security Schemes safe to use as C# variable name by @smoerijf in christianhelle/refitter#977 * Enhance schema alias handling and property name generation by @christianhelle in christianhelle/refitter#996 * Verify alias handling and sanitize PascalCase properties by @christianhelle in christianhelle/refitter#997 * Harden .refitter settings deserialization and output path handling by @christianhelle in christianhelle/refitter#1000 * Special-case the default .refitter filename before deriving OutputFilename by @coderabbitai[bot] in christianhelle/refitter#1002 * [v2.0 audit] Fix pre-release regressions from #1057 by @christianhelle in christianhelle/refitter#1064 * Resolve high-severity audit findings from #1057 by @christianhelle in christianhelle/refitter#1067 * [v2.0 audit] Close remaining verified #1057 regressions by @christianhelle in christianhelle/refitter#1070 * Harden generation flows by @christianhelle in christianhelle/refitter#1071 * Breaking changes and Migration Guide for v2.0.0 by @christianhelle in christianhelle/refitter#1009 * Handle equivalent duplicate schemas in multi-spec merge by @christianhelle in christianhelle/refitter#1076 * Tighten warning handling across Refitter builds by @christianhelle in christianhelle/refitter#1079 * Fix default solution path and update .NET version in VS Code config by @christianhelle in christianhelle/refitter#1082 * Fix docker smoke tests by @christianhelle in christianhelle/refitter#1081 * Sanitize malformed schema type names without renaming clean schemas by @christianhelle in christianhelle/refitter#1085 ... (truncated) Commits viewable in [compare view](christianhelle/refitter@1.7.3...2.0.0). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>



Users with versioned APIs (e.g., V1 + V2) had to maintain separate generated clients and DI registrations per spec. This adds
openApiPathsto.refittersettings, merging multiple OpenAPI documents into one generated client.Description:
Core changes
RefitGeneratorSettings— addsOpenApiPaths string[]property; takes precedence overOpenApiPathwhen non-empty (including when both are specified)OpenApiDocumentFactory— addsCreateAsync(IEnumerable<string>)overload with explicit null/empty argument validation (ArgumentException), and aMerge()method that combinesPaths,Components.Schemas(with null-safe access),Definitions(OpenAPI 2.x), andTagsacross documents; first document in the array wins on duplicatesRefitGenerator—GetOpenApiDocument()uses null-safeOpenApiPaths is { Length: > 0 }pattern to route to the merge path whenOpenApiPathsis populatedSettingsValidator— accepts eitheropenApiPathor a non-emptyopenApiPathsarray as valid; always uses the firstOpenApiPathsentry for validation when the array is non-empty (regardless ofopenApiPath); null-guardsOpenApiPathsto handle"openApiPaths": nullin settings filesUsage
{ "openApiPaths": [ "PetstoreV1.yaml", "PetstoreV2.yaml" ] }Generates a single interface containing operations and contracts merged from all specified specs:
Example OpenAPI Specifications:
Example generated Refit interface
Original prompt
This section details on the original issue you should resolve
<issue_title>Generate the client from multiple versions</issue_title>
<issue_description>Is your feature request related to a problem? Please describe.
I have a V1 and a V2 specification of the same Api. If I now want to generate a client, I have to do this for each version and also maintain the interface and dependency injection twice.
Describe the solution you'd like
Generate a client that contains both versions. For example, the method name could contain the required version.
.refitter File
{ "openApiPaths": [ "PetstoreV1.yaml", "PetstoreV2.yaml", ] }Generated Interface:
Additional context
OpenAPI - PetstoreV1
OpenAPI - PetstoreV2