This is first naive version of Giraffe server generator from OpenAPI specification
I believe in "contract-first" approach, and your OpenAPI spec is basically contract for your API
Backend code is just an implementation of this contract and client doesn't really want to know about it
Neither should you, so this library will help you with that
It follows Myriad approach and defines MSBuild target to generate code based on input
Example project is available to check here
- Creating models from OpenAPI schemas
- records generated from schema definitions with all data types from spec
- handlers in generated webApp should support these types
- default values support for primitive types
-
oneOf
support -
anyOf
support -
allOf
support -
discriminator
support -
not
won't be supported - validation support (#33)
- NodaTime support opt-in (#32)
- Multiple responses from one endpoint
- Creating endpoints with support for bindings
- path
- query
- body
- header (#35)
- cookie (#35)
- content-type negotiated body (#36)
- binding error handling
- Add XML comments on top of endpoint from OpenAPI descriptions
- Support authentication (best efforts)
- Support JSON/XML (de-)serialization
- JSON
- XML
- Add nuget
GiraffeGenerator.Sdk
- Create OpenAPI spec file
- Add generated file to your project file:
<Compile Include="Generated.fs">
<OpenApiSpecFile>spec.yaml</OpenApiSpecFile>
</Compile>
- Build project to generate the file
- Implement interface defined in this file and register your implementation in AspNetCore DI
- If you want to customize validation for some or all of your models, you have two extension points:
IGiraffeValidator<'model>
- replaces all generated validation for the'model
type. Note: doesn't replace the validation for nested complex types (objects, arrays, options). Provide an implementation for them explicitely if you want to replace validation for them too.IGiraffeAdditionalValidator<'model>
- adds more validation to eitherIGiraffeValidator<'model>
or generated validation
- Note for people migrating from/to *nix:
System.ComponentModel.DataAnnotations.RangeAttribute
used by validation produces a different text representation for Double.*Infinity on *nix: "Infinity" instead of infinity unicode symbol (ÝE;) - May require serializer configuration to support mapping of absent and null values from/to Optionon<_>
- May require serializer configuration to throw on absent required properties
All configuration is done by adding more child tags to Compile object. There are two types of configuration: parameterless (flags) and parameterfull (parameters).
flag
is specified like <OpenApiFlag>true</OpenApiFlag>
: absense of tag or any content except for true
is treated as false
parameter
is passed as tag content like <OpenApiValue>your value</OpenApiValue>
Example of both:
<Compile Include="Generated.fs">
<OpenApiSpecFile>spec.yaml</OpenApiSpecFile>
<OpenApiSomeFlag>true</OpenApiSomeFlag>
</Compile>
Defined as parameter OpenApiModuleName
Defined as flag OpenApiAllowUnqualifiedAccess
Enabled by OpenApiUseNodaTime
flag.
Has optional parameter OpenApiMapDateTimeInto
which controls generated type for date-time
OpenAPI string format.
Has four possible values:
instant
local-date-time
offset-date-time
(default)zoned-date-time
Adds support for the following custom string formats:
Format | NodaTime type | Supports default values (format) |
---|---|---|
local-date | LocalDate | [x] uuuu'-'MM'-'dd (c) |
date | LocalDate | [x] (as above) |
date-time | Differs (see OpenApiMapDateTimeInto ) |
[~] By configured format |
instant | Instant | [x] uuuu'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFFF'Z' |
local-time | LocalTime | [x] HH':'mm':'ss.FFFFFFFFF |
time | LocalTime | [x] (as above) |
local-date-time | LocalDateTime | [x] uuuu'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFFF (c) |
offset-date-time | OffsetDateTime | [x] uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<Z+HH:mm> (c) |
zoned-date-time | ZonedDateTime | [ ] No |
offset | Offset | [x] general pattern, e.g. +05 or -03:30 |
time-offset | Offset | [x] (as above) |
duration | Duration | [x] -H:mm:ss.FFFFFFFFF |
period | Interval | [x] ISO8601 Duration (round-trip) |
time-zone | DateTimeZone | [x] IANA Tzdb identifier |
date-time-zone | DateTimeZone | [x] (as above) |
Usage requires installation of NodaTime 3+ nuget at least. For some content types of bodies containing NodaTime types additional packages may be needed.
Note that zoned-date-time cannot be passed in query string or path parameters by default. Also note that (de)serialization duration format differs for query string binding and Json.NET serialization by default. See this test for more details.
- It parses OpenAPI spec with package
Microsoft.OpenApi.Readers
to intermediate representation (IR) - Then it creates F# AST based on that IR
- Finally it produces source code file with help of
Fantomas
- Restore tools:
dotnet tool restore
dotnet pwsh build.ps1
At this stage there is no NuGet package publishing and packages are being published locally
To consume them in Example
project there is local NuGet.Config
with local repo added
After first full build&pack
you could delete Generated.fs
file from Example
project and build it again to see that it actually generates on build
- Make sure you have nuget API key set in
GIRAFFE_GENERATOR_NUGET
env - Update version in
Directory.build.props
- Put whatever you haven't put into
Unreleased
section of CHANGELOG - Run
./publish.ps1
- It will ask you for random number (as protection from accidental runs)
- It will ask you whether you changed the version and updated CHANGELOG
- It will parse new version from the
Directory.build.props
- Hard reset with git clean to latest master (stashing and poping props and CHANGELOG)
- Run
./build.ps1
(compilation + test) - Check that tag with that version doesn't exist
- Check that last version in changelog is lower
- Will update CHANGELOG on major and minor (not patch) updates
- It will replace
Unreleased
section with new version and will put a date on it - Put link to the bottom section called Changes (as git diff)
- It will replace
- Will commit "release vX.Y.Z" with a tag
- Will push artifacts to nuget at the end