OpenAPI 3 Generator
2020-09-10
openapi3gen is based on mikunalpha/goas.
Generates basic OpenAPI Specification json file based on comments in Go.
Enhancements
- Migrated to
github.com/urfave/cli/v2. - Added support for
$GOPATHwith multiple values (multiple directories). First path is used in such cases. - Reworked command line arguments and added more flexibility. All arguments have a default value. See Usage
- Tied
API Info and Serversspecification to one function rather than whole main file. Both the function name and the main file can be specified through command line arguments. See Usage - Added support for multiline
@Descriptioncomments. - Added support to set optional example for parameters in
path,query,headerandcookie. See Adding Path - Paramenter - Reworked
@Routeto be more natural and added an optional ordering flag. See Adding Path - Route - Removed hardcoded default security scheme. Generated OAS 3.0 json is no longer tied to that.
- New README to show new features and also highlight old undocumented features.
-
Merged with Jason's work, which includes (from what I've seen)
- Switching to
github.com/sirkon/goproxy/gomodfromgithub.com/mikunalpha/go-module - Improved handling of Go module. Command line flag
base-pathto specify base path for replaced module
- Switching to
- Renamed module to
gitlab.com/innostream/openapi3gen - Refactored
CreateOASFilefunction into 3 separate functions: parse comments, generate JSON and create file. - Added simple test for normal behaviour
- Merged new commit in Goas: "Replace \ in / for import names"
- Merged new commit in Goas: "Parse and output Security related fields"
- Extended implementation to include tag definitions (
@TagDefinition) - Added support for Redoc vender extensions: @XLogo, @XDisplayName, @XTagGroup, @XCodeSample
- Added support for numeric or string enums while defining Components
Limitations
- Only supports Go module.
- Anonymous struct field is not supported.
- No support for security schemas - need to merge changes in latest Goas version
- Limited support for OAS 3.0 tags (more details below).
base-pathlimited to one path.
Install
Copy the project to your $GOPATH (or $GOROOT) then run
go get gitlab.com/innostream/openapi3gen
Adding API Info and Servers
The API info and servers are specified per template below. By default, openapi3gen will search for the comments on the main function in package main (See Usage for more info)
- Note that the braces are not part of the syntax, but instead denote a value.
- All tags are case-insensitive (a tag is something that starts with
@) - The order of the tags is not enforced.
- Multiple
@Descriptionand@Servertags are allowed. @SecuritySchemecan have different parameters as defined under Security.-
@TagDefinitionis optional and can be used to give additional information on@Tags used to group routes.x-displayNameis an optional extension for redoc.
-
@XTagGroupis an optional extension for redoc. It is used to group tags in the side menu.- !!! If you are going to use x-tagGroups, please make sure you add all tags to a group, since a tag that is not in a group, will not be displayed at all!
@XLogois an optional extension for redoc. If specified, the url and background color is required.-
@XCodeSampleis an optional extension for code to be rendered on the right panel of ReDoc. Value forlangone of https://github.com/github/linguist/blob/master/lib/linguist/popular.yml// @Version {text} // @Title {text} // @Description {text} // @ContactName {text} // @ContactEmail {email address} // @ContactURL {url} // @TermsOfServiceUrl {url} // @LicenseName {text} // @LicenseURL {url} // @Server {url} {text} // @SecurityScheme {name} {type} {parameters} // @Security {scheme name} {space separates list of scopes} // for OAuth2 only: // @SecurityScope {schema name} {scope code} {scope description}` // @TagDefinition {name} "{description}" "{x-displayName}" // @XTagGroup "{group name}" {space separated list of tags} // @XLogo "{url}" "{hex background color}" "{href}" "{alt text}" // @XCodeSample {lang} {label} {source code}
There's special handling in place for @Description tags. It can span over multiple lines (see example below).
Example
// @Version 1.0.0
// @Title The Matrix API
// @Description API usually works as expected. But sometimes its not true.
// Good news though - we know this is a continuation of the description
//
// And we also know that the previous comment means you want a new line in the description.
// \nThis works too.
// @ContactName John Wick
// @ContactEmail jwick@assass.in
// @ContactURL http://someurl.oxox
// @TermsOfServiceUrl http://someurl.oxox
// @LicenseName MIT
// @LicenseURL https://en.wikipedia.org/wiki/MIT_License
// @Server http://www.fake.com Server-1
// @Server http://www.fake2.com Server-2
// @SecurityScheme AuthorizationHeader oauth2ClientCredentials /oauth/token
// @Security AuthorizationHeader read_user write_user
// for OAuth2 only:
// @SecurityScope MyApiAuth read_user Read a user from the system
// @SecurityScope MyApiAuth write_user Write a user to the system
// @TagDefinition user "Some descripton of why this exists" "The User"
// @TagDefinition message "Some high level descripton of messages" "The Message"
// @XTagGroup "Common" user message
// @XLogo "https://upload.wikimedia.org/wikipedia/commons/7/79/John_Wick_2.png" "#fff" "https://redocly.github.io/redoc/" "The Logo"
// @XCodeSample Go go fmt.Println("“He Shot My Dog.” “I Get It.”")
Security (copied from latest 24/04/20 commit on Goas)
If authorization is required, you must define security schemes using @SecurityScheme and then apply those to the API by adding @Security. For OAuth2 security schemes, it is possible to define scopes using @SecurityScope.
All examples in this section use MyApiAuth as the name. This name can be anything you chose; multiple named schemes are supported.
Each scheme must have its own name, except for OAuth2 schemes - OAuth2 supports multiple schemes by the same name.
A number of different types is supported, they all have different parameters:
| Type | Description | Parameters | Example |
|---|---|---|---|
| HTTP | A HTTP Authentication scheme using the Authorization header |
scheme: any HTTP Authentication scheme | @SecurityScheme MyApiAuth basic |
| APIKey | Authorization by passing an API Key along with the request | in: Location of the API Key, options are header, query and cookie. name: The name of the field where the API Key must be set |
@SecurityScheme MyApiAuth apiKey header X-MyCustomHeader |
| OpenIdConnect | Delegating security to a known OpenId server | url: The URL of the OpenId server | @SecurityScheme MyApiAuth openIdConnect https://example.com/.well-known/openid-configuration |
| OAuth2AuthCode | Using the "Authentication Code" flow of OAuth2 | authorizationUrl, tokenUrl | @SecurityScheme MyApiAuth oauth2AuthCode /oauth/authorize /oauth/token |
| OAuth2Implicit | Using the "Implicit" flow of OAuth2 | authorizationUrl | @SecurityScheme MyApiAuth oauth2Implicit /oauth/authorize |
| OAuth2ResourceOwnerCredentials | Using the "Resource Owner Credentials" flow of OAuth2 | authorizationUrl | @SecurityScheme MyApiAuth oauth2ResourceOwnerCredentials /oauth/token |
| OAuth2ClientCredentials | Using the "Client Credentials" flow of OAuth2 | authorizationUrl | @SecurityScheme MyApiAuth oauth2ClientCredentials /oauth/token |
Any text that is present after the last parameter wil be used as the description. For instance @SecurityScheme MyApiAuth basic Login with your admin credentials.
Once all security schemes have been defined, they must be configured. This is done with the @Security comment.
At the moment, it is only possible to configure security for the entire service.
Adding Paths
openapi3gen will look for all functions annotated with @Route in the module-path (or routes - See Usage for more info). The template for an API path is shown below.
- Note that the braces are not part of the syntax, but instead denote a value.
- All tags are case-insensitive.
- The order of the tags is not enforced.
@Descriptioncan span over multiple comment lines.- All tags related to the path must be in the same comment block.
@Resourcecan be interchanged with@Tag. A consumer can use these tags to group the paths.- Multiple
@Resource,@Tag,@Description,@Param,@Successand@Failureallowed.
// @Title {summary}
// @Description {description}
// @Param {name} {in} {goType} {required} "{description}" "{example}"
// @Success {status} {jsonType} {goType} "{description}"
// @Failure {status} {jsonType} {goType} "{description}"
// @Resource {resource}
// @Route {order index} {method} {path}
func SomeFunction() {
// ...
}
Parameter - @Param
// @Param {name} {in} {goType} {required} "{description}" "{example}"
- {name}: The parameter name.
- {in}: The parameter is in
path,query,form,header,cookie,bodyorfile. - {goType}: The type in go code. This will be ignored when {in} is
file. To specify a component forbody, use the formatpackageName.ExportedStruct - {required}:
true,false,requiredoroptional. - {description}: The description of the parameter. Must be quoted.
- {example}: optional example for parameters in
path,query,headerandcookie. Must be quoted. Example forbodyis handled usingexamplego tag in the struct (see Components)
Response - @Success and @Failure
// @Success {status} {jsonType} {goType} "{description}"
// @Failure {status} {jsonType} {goType} "{description}"
- {status}: The HTTP status code.
- {jsonType}: The value can be
objectorarray. - {goType}: The type in go code.
- {description}: The description of the response. Must be quoted.
- Response example is handled for
structs. Not supported for basic types.
Route - @Route
// @Route {order index} {method} {path}
// @Route {method} {path}
- {order index}: An optional index to order the paths in the json.
@Routewithoutorder indexwill appear at the end of the list, sorted alphabetically. - {method}: The HTTP Method.
-
{path}: The URL path.
Example
// GetHelloName returns a hello message with the given name // @Title getHelloName // @Description Get the Hello message for a given name // // and we support multiline descriptions // @Param name path string true "Name to use in the message" "Ubeid" // @Success 200 object web.MessageResponse "A successful message response" // @Resource messages // @Route 1 get /testapi/getHelloName/{name} func GetHelloName() { ... }
// UpdateMessageTemplate updates a message template // @Title updateMessageTemplate // @Description Updates a message template // @Param messageTemplate body web.MessageTemplate true "The updated message template" // @Success 200 object web.UpdateMessageTemplateResponse "A basic success response" // @Failure 403 object string "You shall not pass!" // @Tag messages // @Route 2 post /testapi/updateMessageTemplate func UpdateMessageTemplate() { ... }
## Components
Only `structs` referenced in the `@Param`, `@Success` and `@Failure` are mapped. Go `struct field tags` are used to describe a field. [encoding/json](https://golang.org/pkg/encoding/json/) package is used to marshall the structs into JSON.
- Use the `json` tag for the field name.
- The `required:"true"` tag can be used to mark a field as required. One can also mark a field as required by setting it in the `json` tag, after the name (comma separated).
- Setting the `json` tag to `"-"` will result in the field being ignored (i.e. it will not appear in the JSON file).
- Use the `description` tag to set a description.
- Use the `example` tag to set an example for the field. The example for the struct is composed of the different examples set in the struct tags. **The library will not assume default values for fields with missing `example` tag.**
- Use the `enum` tag to set a list of enums. Note: The list is parsed based on the type of the field. **Type mismatch will be ignored rather than breaking the flow.**
- Note: the package name is used when referencing a struct. E.g.
```// @Success 200 object web.MessageResponse "A successful message response"```
```go
package web
type MessageTemplate struct {
ignored bool `json:"-"`
ID int `json:"id,required" example:"0" description:"ID for message to be updated"`
Message string `json:"message" require:"true" example:"Hello ##NAME##" description:"Updated message template"`
Priority string `json:"priority" require:"true" example:"HIGH" description:"Priority level of the message\n * LOW = lowest of the low etc.." enum:"LOW,NORMAL,HIGH"`
Queue int `json:"queue,omitempty" enum:"0,1,2,3,4,5" example:"0"`
}
type UpdateMessageTemplateResponse struct {
Success bool `json:"success" example:"true" description:"Status of update"`
}
type MessageResponse struct {
Message string `json:"message" example:"Hello John" description:"Message for the given user name"`
GivenName string `json:"givenName" example:"John" description:"The user name"`
}
Mismatched enum types are ignored rather than breaking the flow.
There's currently no way to give a description for a whole struct. Sidenote: Can consider a clever use of
json:"-"on an unexported field to set it.
Usage
// using all default values
openapi3gen
// example: all flags except debug
openapi3gen --module-path . --info-file ./main.go --info-method main --routes ./api/ --output myapi.json
// using aliases
openapi3gen --mp . --if ./main.go --im main --r ./api/ --o myapi.json
Available flags:
-
--module-path,--mp: The directory wherego.modis found.Defaults to
current directory. -
--info-file,--if: The relative path to the file whereAPI Info and Serversare set.Defaults to a file in the
module-paththat haspackage mainandfunc main(). -
--info-method,--im: The function on which theAPI Info and Serverscomments are set.Defaults to
main. -
--routes,--r: The directory whereRoutesare set (it can be in different files).Defaults to
module-path --base-path,--bp: Base path for replaced module-
Defaults to
current directory -
--output,--o: The output file name.Defaults to
oas.json --debug,--d: Show debug message.