1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
package openapi3
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/go-openapi/jsonpointer"
)
// T is the root of an OpenAPI v3 document
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#openapi-object
type T struct {
Extensions map[string]interface{} `json:"-" yaml:"-"`
OpenAPI string `json:"openapi" yaml:"openapi"` // Required
Components *Components `json:"components,omitempty" yaml:"components,omitempty"`
Info *Info `json:"info" yaml:"info"` // Required
Paths *Paths `json:"paths" yaml:"paths"` // Required
Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"`
Tags Tags `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
visited visitedComponent
}
var _ jsonpointer.JSONPointable = (*T)(nil)
// JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable
func (doc *T) JSONLookup(token string) (interface{}, error) {
switch token {
case "openapi":
return doc.OpenAPI, nil
case "components":
return doc.Components, nil
case "info":
return doc.Info, nil
case "paths":
return doc.Paths, nil
case "security":
return doc.Security, nil
case "servers":
return doc.Servers, nil
case "tags":
return doc.Tags, nil
case "externalDocs":
return doc.ExternalDocs, nil
}
v, _, err := jsonpointer.GetForToken(doc.Extensions, token)
return v, err
}
// MarshalJSON returns the JSON encoding of T.
func (doc *T) MarshalJSON() ([]byte, error) {
m := make(map[string]interface{}, 4+len(doc.Extensions))
for k, v := range doc.Extensions {
m[k] = v
}
m["openapi"] = doc.OpenAPI
if x := doc.Components; x != nil {
m["components"] = x
}
m["info"] = doc.Info
m["paths"] = doc.Paths
if x := doc.Security; len(x) != 0 {
m["security"] = x
}
if x := doc.Servers; len(x) != 0 {
m["servers"] = x
}
if x := doc.Tags; len(x) != 0 {
m["tags"] = x
}
if x := doc.ExternalDocs; x != nil {
m["externalDocs"] = x
}
return json.Marshal(m)
}
// UnmarshalJSON sets T to a copy of data.
func (doc *T) UnmarshalJSON(data []byte) error {
type TBis T
var x TBis
if err := json.Unmarshal(data, &x); err != nil {
return unmarshalError(err)
}
_ = json.Unmarshal(data, &x.Extensions)
delete(x.Extensions, "openapi")
delete(x.Extensions, "components")
delete(x.Extensions, "info")
delete(x.Extensions, "paths")
delete(x.Extensions, "security")
delete(x.Extensions, "servers")
delete(x.Extensions, "tags")
delete(x.Extensions, "externalDocs")
if len(x.Extensions) == 0 {
x.Extensions = nil
}
*doc = T(x)
return nil
}
func (doc *T) AddOperation(path string, method string, operation *Operation) {
if doc.Paths == nil {
doc.Paths = NewPaths()
}
pathItem := doc.Paths.Value(path)
if pathItem == nil {
pathItem = &PathItem{}
doc.Paths.Set(path, pathItem)
}
pathItem.SetOperation(method, operation)
}
func (doc *T) AddServer(server *Server) {
doc.Servers = append(doc.Servers, server)
}
func (doc *T) AddServers(servers ...*Server) {
doc.Servers = append(doc.Servers, servers...)
}
// Validate returns an error if T does not comply with the OpenAPI spec.
// Validations Options can be provided to modify the validation behavior.
func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if doc.OpenAPI == "" {
return errors.New("value of openapi must be a non-empty string")
}
var wrap func(error) error
wrap = func(e error) error { return fmt.Errorf("invalid components: %w", e) }
if v := doc.Components; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
}
wrap = func(e error) error { return fmt.Errorf("invalid info: %w", e) }
if v := doc.Info; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
} else {
return wrap(errors.New("must be an object"))
}
wrap = func(e error) error { return fmt.Errorf("invalid paths: %w", e) }
if v := doc.Paths; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
} else {
return wrap(errors.New("must be an object"))
}
wrap = func(e error) error { return fmt.Errorf("invalid security: %w", e) }
if v := doc.Security; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
}
wrap = func(e error) error { return fmt.Errorf("invalid servers: %w", e) }
if v := doc.Servers; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
}
wrap = func(e error) error { return fmt.Errorf("invalid tags: %w", e) }
if v := doc.Tags; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
}
wrap = func(e error) error { return fmt.Errorf("invalid external docs: %w", e) }
if v := doc.ExternalDocs; v != nil {
if err := v.Validate(ctx); err != nil {
return wrap(err)
}
}
return validateExtensions(ctx, doc.Extensions)
}
|