opier/openapi/openapi.go

262 lines
6.1 KiB
Go

package openapi // import "udico.de/uditaren/opier/openapi"
import (
"fmt"
"gopkg.in/yaml.v3"
"os"
"strings"
"unicode"
)
type OpenAPI struct {
Version string `yaml:"openapi"`
Info struct {
Title string
Version string
Description string
}
Paths map[string]*PathItem `yaml:"paths"`
Components *Components
}
func Load(fname string) (*OpenAPI, error) {
f, err := os.Open(fname)
if err != nil {
return nil, err
}
tRet := &OpenAPI{}
tY := yaml.NewDecoder(f)
err = tY.Decode(tRet)
return tRet, err
}
// PathItem Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints.
// The path itself is still exposed to the documentation viewer but they will not know which operations and parameters
// are available.
type PathItem struct {
// Allows for an external definition of this path item. The referenced structure MUST be in the format of a
// PathItem Object. In case a PathItem Object field appears both in the defined object and the referenced object,
// the behavior is undefined.
Ref string `yaml:"$ref"`
// An optional, string summary, intended to apply to all operations in this path.
Summary string
// An optional, string description, intended to apply to all operations in this path. CommonMark syntax MAY be
// used for rich text representation.
Description string
Get *Operation
Put *Operation
Post *Operation
Delete *Operation
Options *Operation
Head *Operation
Patch *Operation
Trace *Operation
}
func (p PathItem) Operations() map[string]*Operation {
tRet := make(map[string]*Operation)
if p.Get != nil {
tRet["get"] = p.Get
}
if p.Put != nil {
tRet["put"] = p.Put
}
if p.Post != nil {
tRet["post"] = p.Post
}
if p.Delete != nil {
tRet["delete"] = p.Delete
}
if p.Options != nil {
tRet["options"] = p.Options
}
if p.Head != nil {
tRet["head"] = p.Head
}
if p.Patch != nil {
tRet["patch"] = p.Patch
}
if p.Trace != nil {
tRet["trace"] = p.Trace
}
return tRet
}
// Operation describes a single API operation on a path.
type Operation struct {
// A list of tags for API documentation control. Tags can be used for logical grouping of operations
// by resources or any other qualifier.
Tags []string
// A short summary of what the operation does.
Summary string
// A verbose explanation of the operation behavior. CommonMark syntax MAY be used for rich text representation.
Description string
// Additional external documentation for this operation.
//externalDocs
// Unique string used to identify the operation. The id MUST be unique among all operations described in the API.
// The operationId value is case-sensitive. Tools and libraries MAY use the operationId to uniquely identify an
// operation, therefore, it is RECOMMENDED to follow common programming naming conventions.
OperationId string `yaml:"operationId"`
//Parameters [] ...
// requestBodyesObject // required
// callba
// Responses Responscks
Deprecated bool
// A declaration of which security mechanisms can be used for this operation.
// The list of values includes alternative security requirement objects that can be used. Only one of the security
// requirement objects need to be satisfied to authorize a request. To make security optional, an empty security
// requirement ({}) can be included in the array. This definition overrides any declared top-level security.
// To remove a top-level security declaration, an empty array can be used.
Security []map[string][]string `yaml:"security"`
// An alternative server array to service this operation. If an alternative server object is specified at the Path
// Item Object or Root level, it will be overridden by this value.
// Servers
}
type ResponsesObject struct {
// ...
}
type Components struct {
//SecuritySchemas
//Responses map[string, ResponseObject]
Parameters map[string]Parameter
Schemas map[string]Schema
}
type ParameterLocation string
const (
QUERY ParameterLocation = "query"
HEAD = "header"
PATH = "path"
COOKIE = "cookie"
)
type Parameter struct {
// only if this a reference object instead of a parameter obect
Ref string `yaml:"$ref"`
Name string
In ParameterLocation
Description string
Required bool
Deprecated bool
AllowEmptyValue bool `yaml:"allowEmptyValue"`
///--- either
Style string
Explode bool
AllowReserved bool `yaml:"allowReserved"`
Schema *Schema
// example
// examples
///--- or
Content map[string]*MediaType
}
type Schema struct {
// may be a schema reference:
Ref string `yaml:"$ref"`
Type string
// when type==array:
Items *Schema
// when type==object
Properties map[string]*Schema
Format string
Minimum int
Maximum int
Default interface{}
Enum []interface{}
XEnumVarnames []string `yaml:"x-enum-varnames"`
Required []string
Description string
}
func (s Schema) TypeOrReference() string {
if s.Ref != "" {
return s.Ref
}
return s.Type
}
func (s Schema) GoType() string {
tRet := s.Type
if s.Ref != "" {
idx := strings.LastIndex(s.Ref, "/")
tRet = "*" + s.Ref[idx+1:]
} else {
switch s.Type {
case "integer":
tRet = "int"
case "boolean":
tRet = "bool"
case "array":
tRet = fmt.Sprintf("[]%v", s.Items.GoType())
}
}
return tRet
}
// EnumNames returns the String representations for this enums values and the length of the longest name as well.
func (s Schema) EnumNames() ([]string, int) {
tLen := 0
tRet := make([]string, len(s.Enum), len(s.Enum))
for k, v := range s.Enum {
tName := NormalizeName(fmt.Sprintf("%v", v))
if len(s.XEnumVarnames) == len(s.Enum) {
// read the desired enum name instead
tName = s.XEnumVarnames[k]
}
tRet[k] = tName
if len(tName) > tLen {
tLen = len(tName)
}
}
return tRet, tLen
}
type Referencable interface {
}
type MediaType struct {
}
func NormalizeName(aName string) string {
tRet := &strings.Builder{}
capitalizeNext := true
for _, rune := range aName {
if capitalizeNext {
tRet.WriteRune(unicode.ToUpper(rune))
capitalizeNext = false
} else {
if rune == '-' {
capitalizeNext = true
} else {
tRet.WriteRune(rune)
}
}
}
return tRet.String()
}