Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13a76f0
throw openapi specification parsing errors
Fresa Jan 9, 2026
788cb0e
feat(openapi): add v3 visitors
Fresa Jan 9, 2026
846fa34
generate schemas from v3 specs
Fresa Jan 9, 2026
e64e558
test(v3): generate with a v3.0.3 spec
Fresa Jan 10, 2026
7b89caf
test: v3.1
Fresa Jan 10, 2026
31d1266
refactor: consolidate generation exception handling into one analyzer…
Fresa Jan 10, 2026
c61a98d
remove redundant is required directive
Fresa Jan 11, 2026
bdbcbb7
clean up
Fresa Jan 11, 2026
e220edb
feat(parameter): support parameter value parsing for openapi 3.0 and 3.1
Fresa Jan 13, 2026
aac323f
move openapi 2.0 example api
Fresa Jan 13, 2026
f124d0e
adjust namespaces
Fresa Jan 13, 2026
72e59ed
test(openapi30): add example api and integration tests
Fresa Jan 13, 2026
88f2946
generate parameter specification for correct openapi spec version
Fresa Jan 14, 2026
acf9527
fix(parameter): remove invalid $ character in value
Fresa Jan 14, 2026
ea938cd
fix(validation): push schema location explicitly to set proper locati…
Fresa Jan 15, 2026
7f431e7
fix(response): write response header specifications using the correct…
Fresa Jan 15, 2026
01ac4be
fix(response): use the proper serializer
Fresa Jan 15, 2026
d257b09
refactor: simplify writing response headers
Fresa Jan 15, 2026
efee8b3
test(openapi31): add integration test for openapi 3.1
Fresa Jan 16, 2026
ea20fa2
fix validation errors in openapi specs
Fresa Jan 16, 2026
4390395
ci: lint openapi specs
Fresa Jan 16, 2026
eea607d
ci: glob all openapi specs
Fresa Jan 16, 2026
b2ff9d6
fix response header code formatting
Fresa Jan 17, 2026
c93975a
avoid instantiating the same parameter specification everytime a para…
Fresa Jan 17, 2026
be8eccf
refactor(response): separate write and validate in order to validate …
Fresa Jan 17, 2026
0173e45
let user override the response validation process
Fresa Jan 17, 2026
c018fd1
let user override validation level per operation
Fresa Jan 17, 2026
abf819f
refactor: move parameter spec version to generated constant
Fresa Jan 17, 2026
8ccbcb4
doc(openapi): include supported openapi version in README
Fresa Jan 17, 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
21 changes: 21 additions & 0 deletions .github/workflows/lint-openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Lint OpenAPI Specs

on:
push:
branches:
- "**"
tags-ignore:
- '**'

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install vacuum
run: curl -fsSL https://quobix.com/scripts/install_vacuum.sh | sh

- name: Lint OpenAPI specs
run: |
vacuum lint --globbed-files="*/**/openapi.json"
102 changes: 100 additions & 2 deletions OpenAPI.WebApiGenerator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,138 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAPI.WebApiGenerator", "src\OpenAPI.WebApiGenerator\OpenAPI.WebApiGenerator.csproj", "{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Api", "tests\Example.Api\Example.Api.csproj", "{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.OpenApi20", "tests\Example.OpenApi20\Example.OpenApi20.csproj", "{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Api.IntegrationTests", "tests\Example.Api.IntegrationTests\Example.Api.IntegrationTests.csproj", "{2A585540-1B80-4848-9A93-E0286758E2E0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.OpenApi20.IntegrationTests", "tests\Example.OpenApi20.IntegrationTests\Example.OpenApi20.IntegrationTests.csproj", "{2A585540-1B80-4848-9A93-E0286758E2E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAPI.WebApiGenerator.Tests", "tests\OpenAPI.WebApiGenerator.Tests\OpenAPI.WebApiGenerator.Tests.csproj", "{8044D11A-B0D2-400A-B2A1-8C50E396073A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.OpenApi30", "tests\Example.OpenApi30\Example.OpenApi30.csproj", "{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.OpenApi30.IntegrationTests", "tests\Example.OpenApi30.IntegrationTests\Example.OpenApi30.IntegrationTests.csproj", "{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{F4FDC271-0CCB-4171-87B8-937A6E1CBF9A}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
.github\workflows\cd.yml = .github\workflows\cd.yml
LICENSE = LICENSE
.github\workflows\lint-openapi.yml = .github\workflows\lint-openapi.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.OpenApi31", "tests\Example.OpenApi31\Example.OpenApi31.csproj", "{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.OpenApi31.IntegrationTests", "tests\Example.OpenApi31.IntegrationTests\Example.OpenApi31.IntegrationTests.csproj", "{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Debug|x64.ActiveCfg = Debug|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Debug|x64.Build.0 = Debug|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Debug|x86.ActiveCfg = Debug|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Debug|x86.Build.0 = Debug|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Release|Any CPU.Build.0 = Release|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Release|x64.ActiveCfg = Release|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Release|x64.Build.0 = Release|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Release|x86.ActiveCfg = Release|Any CPU
{E2935A8A-ED91-4A1D-BEF4-08D916A7ED07}.Release|x86.Build.0 = Release|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Debug|x64.ActiveCfg = Debug|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Debug|x64.Build.0 = Debug|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Debug|x86.ActiveCfg = Debug|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Debug|x86.Build.0 = Debug|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Release|Any CPU.Build.0 = Release|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Release|x64.ActiveCfg = Release|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Release|x64.Build.0 = Release|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Release|x86.ActiveCfg = Release|Any CPU
{790AE9B7-F3EA-459C-BAB2-D75E903D9B39}.Release|x86.Build.0 = Release|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Debug|x64.ActiveCfg = Debug|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Debug|x64.Build.0 = Debug|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Debug|x86.ActiveCfg = Debug|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Debug|x86.Build.0 = Debug|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Release|Any CPU.Build.0 = Release|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Release|x64.ActiveCfg = Release|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Release|x64.Build.0 = Release|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Release|x86.ActiveCfg = Release|Any CPU
{2A585540-1B80-4848-9A93-E0286758E2E0}.Release|x86.Build.0 = Release|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Debug|x64.ActiveCfg = Debug|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Debug|x64.Build.0 = Debug|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Debug|x86.ActiveCfg = Debug|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Debug|x86.Build.0 = Debug|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Release|Any CPU.Build.0 = Release|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Release|x64.ActiveCfg = Release|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Release|x64.Build.0 = Release|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Release|x86.ActiveCfg = Release|Any CPU
{8044D11A-B0D2-400A-B2A1-8C50E396073A}.Release|x86.Build.0 = Release|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Debug|x64.ActiveCfg = Debug|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Debug|x64.Build.0 = Debug|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Debug|x86.ActiveCfg = Debug|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Debug|x86.Build.0 = Debug|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Release|Any CPU.Build.0 = Release|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Release|x64.ActiveCfg = Release|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Release|x64.Build.0 = Release|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Release|x86.ActiveCfg = Release|Any CPU
{B1C2D3E4-F5A6-47B8-9C0D-1E2F3A4B5C6D}.Release|x86.Build.0 = Release|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Debug|x64.ActiveCfg = Debug|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Debug|x64.Build.0 = Debug|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Debug|x86.ActiveCfg = Debug|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Debug|x86.Build.0 = Debug|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Release|Any CPU.Build.0 = Release|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Release|x64.ActiveCfg = Release|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Release|x64.Build.0 = Release|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Release|x86.ActiveCfg = Release|Any CPU
{C2D3E4F5-A6B7-48C9-0D1E-2F3A4B5C6D7E}.Release|x86.Build.0 = Release|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Debug|x64.ActiveCfg = Debug|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Debug|x64.Build.0 = Debug|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Debug|x86.ActiveCfg = Debug|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Debug|x86.Build.0 = Debug|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Release|Any CPU.Build.0 = Release|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Release|x64.ActiveCfg = Release|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Release|x64.Build.0 = Release|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Release|x86.ActiveCfg = Release|Any CPU
{FF8E3B7A-20A5-4702-871C-D8ABC6D82F09}.Release|x86.Build.0 = Release|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Debug|x64.ActiveCfg = Debug|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Debug|x64.Build.0 = Debug|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Debug|x86.ActiveCfg = Debug|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Debug|x86.Build.0 = Debug|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Release|Any CPU.Build.0 = Release|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Release|x64.ActiveCfg = Release|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Release|x64.Build.0 = Release|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Release|x86.ActiveCfg = Release|Any CPU
{FE9CE314-77B1-4064-B4E3-F2FCE3EDB63C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
EndGlobal
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ Generates scaffolding for Web APIs from OpenAPI specifications.

The generated functionality will route, serialize/deserialize and validate payloads according to the specification.

Supported OpenAPI version:
- [2.0](https://spec.openapis.org/oas/v2.0.html)
- [3.0.0](https://spec.openapis.org/oas/v3.0.0.html)
- [3.0.1](https://spec.openapis.org/oas/v3.0.1.html)
- [3.0.2](https://spec.openapis.org/oas/v3.0.2.html)
- [3.0.3](https://spec.openapis.org/oas/v3.0.3.html)
- [3.0.4](https://spec.openapis.org/oas/v3.0.4.html)
- [3.1.0](https://spec.openapis.org/oas/v3.1.0.html)
- [3.1.1](https://spec.openapis.org/oas/v3.1.1.html)
- [3.1.2](https://spec.openapis.org/oas/v3.1.2.html)

API frameworks supported:
- [Minimal API](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis)

Expand Down Expand Up @@ -34,11 +45,11 @@ https://www.nuget.org/packages/WebApiGenerator.OpenAPI
```
<ItemGroup>
<PackageReference Include="Corvus.Json.ExtendedTypes" Version="4.3.13" />
<PackageReference Include="ParameterStyleParsers.OpenAPI" Version="1.1.0" />
<PackageReference Include="ParameterStyleParsers.OpenAPI" Version="1.4.0" />
</ItemGroup>
```
* Corvus.Json.ExtendedTypes >= 4.0.0
* ParameterStyleParsers.OpenAPI >= 1.1.0
* ParameterStyleParsers.OpenAPI >= 1.4.0

4. Compile the project.

Expand All @@ -52,9 +63,14 @@ app.MapOperations();
app.Run();
```

See [Example.Api](tests/Example.Api) as an example.
Examples:
- [OpenAPI 2.0](tests/Example.OpenApi20)
- [OpenAPI 3.0](tests/Example.OpenApi30)
- [OpenAPI 3.1](tests/Example.OpenApi31)

All specifications mostly generate similar abstractions. What might differ is the location of generated resources, which follows the respective structure of the OpenAPI specification, and the JSON types, which are based on the respective schema version.

**Note**: The Example.Api references the generator through a project reference. Use a package reference instead as described above.
**Note**: The Examples reference the generator through a project reference. Use a package reference instead as described above.

## Implementing an [API Operation](https://swagger.io/specification/#operation-object)
The generator generates stubbed partial classes for any operation handlers (`Foo.Bar.Operation.Handler.cs`) if there are none existing in the project and logs it with a compiler warning (AF1001). The classes should be copied into source control and the operation methods implemented. The operation methods have a familiar request/response design:
Expand Down
3 changes: 1 addition & 2 deletions src/OpenAPI.WebApiGenerator/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
AF0001 | Compiler | Error | ApiGenerator
AF1001 | Api | Warning | EndpointGenerator
CRV1001 | JsonSchemaCodeGenerator | Error | ApiGenerator
AF1001 | Api | Warning | EndpointGenerator
Loading