DEPRECATED
This tool was superseeded by caffoa.net.
Use caffoa.net as nuget tool to create your open api files.
caffoa: Create Azure Function From Open Api
Tool to autogenerate azure function templates for .NET from openapi declaration.
Instead of generating stubs, the goal is to be able to change the api and re-generate the files without overwriting your code.
Currently considered alpha state. If something does not work that you feel should work, create a ticket with your openapi spec.
It uses prance for parsing the openapi spec.
Usage
As code generation needs a lot of configuration, all configuration is done using a config file in yaml format.
The minimal config file is as follows:
config:
version: 2
services:
- apiPath: my-service.openapi.yml
function:
name: MyClassName
namespace: MyNamespace
targetFolder: ./output
model:
namespace: MyNamespace.Model
targetFolder: ./output/Model
You can add multiple services. Also, you can omit either model
or function
if you do not need one of them.
Then, call the tool:
python3 -m caffoa --config path_to_config.yml
Create Azure Function template:
If you specified the function
part in the config file,
the tool will create two files in the specified target folder:
- MyClassNameFunction.generated.cs
- IMyClassNameService.generated.cs
Your job now is to create an implementation for the IMyClassNameService
interface.
Furthermore, you need Dependency Injection to pass your implementation to the constructor of the function.
Now implement all the logic in your implementation of the interface. You can now change your API, and regenerate the generated files without overwriting your code.
Create data objects from schemas
If you specified the model
part in the config file, the tool will generate a file for each schema defined in the components section of the openapi definition. The filename will be the schema name converted to UpperCamelCase with generated.cs added to the end (Example: user
will create a class User
defined in the file User.generated.cs
).
The file will contain a shared class, with all properties of the schema. You can implement a shared class in a different file to add logic to these objects.
Restrictions
- The schema must be defined in the components section.
- Furthermore, schemas may not be nested without reference.
(You can easily overcome this restriction by defining more schemas in the components section and have them reference each other.)
- allOf is implemented as inheritance, and therefore can only handle allOf with one reference and one direct configuration
Advanced configuration options
There are multiple optional configuration options that you can use:
config:
clearGeneratedFiles: true
version: 2
useFactory: false
prefix: "Pre"
suffix: "Suf"
errorFolder: ./output/errors
errorNamespace: MyErrorNamespace
imports:
- MySpecialNamespace
services:
- apiPath: userservice.openapi.yml
config:
prefix:
suffix:
useFactory:
errorFolder:
errorNamespace:
imports:
function:
name: MyClassName
namespace: MyNamespace
targetFolder: ./output
functionsName: MyFunctions
interfaceName: IMyInterface
interfaceNamespace: MyInterfaceNamespace
interfaceTargetFolder: ./output/shared
boilerplate: |
try {
{BASE}
catch(SomethingNotFoundException e) {
return new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(err.Message)
};
}
imports:
- MyNamespace.Exceptions.SomethingNotFoundException
model:
namespace: MyNamespace.Model
targetFolder: ./output/Model
excludes:
- objectToExclude
include:
- objectToInclude
- otherObjectToInclude
imports:
- someImport
prefix:
suffix:
Version 3
If you have a well specified openapi doc, use only json request bodies and returns, and you want strict rules what you get to work with and what you return, you can try out version 3.
Version 3 parses the return and requestBody specifications, and handles the object wrapping for you.
- Request bodies that have well-defined schemas will be deserialized to the object
- Responses that have well-defined schemas will be serialized to Json responses
- The interface will not have IActionResult returns, but need to return the actual object for the method
- The interface will have the actual type that was passed along in the body as parameter
- Errors (400-499) will be implemented as Exceptions, that you can throw in your implementation.
- If you return a JSON Object with your Errors, a derived class will be created for that
- If you have different return codes for one object (e.g. 200 or 201 for a put request), the return of the interface will be (YourObject, int).
Version 3 takes over a lot of boilerplate code for you. Furthermore, it forces you to not cut corners, as you cannot return a different object than the specification calls for.