mirror of
https://github.com/Fishwaldo/huma.git
synced 2025-03-15 11:21:42 +00:00
164 lines
4.7 KiB
Go
164 lines
4.7 KiB
Go
package huma
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/danielgtaylor/huma/schema"
|
|
)
|
|
|
|
// oaContact describes contact information for this API.
|
|
type oaContact struct {
|
|
Name string `json:"name,omitempty"`
|
|
URL string `json:"url,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
}
|
|
|
|
// oaServer describes an OpenAPI 3 API server location
|
|
type oaServer struct {
|
|
URL string `json:"url"`
|
|
Description string `json:"description,omitempty"`
|
|
}
|
|
|
|
// paramLocation describes where in the HTTP request the parameter comes from.
|
|
type paramLocation string
|
|
|
|
// Parameter locations supported by OpenAPI 3
|
|
const (
|
|
inPath paramLocation = "path"
|
|
inQuery paramLocation = "query"
|
|
inHeader paramLocation = "header"
|
|
)
|
|
|
|
// oaParam describes an OpenAPI 3 parameter
|
|
type oaParam struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
In paramLocation `json:"in"`
|
|
Required bool `json:"required,omitempty"`
|
|
Schema *schema.Schema `json:"schema,omitempty"`
|
|
Deprecated bool `json:"deprecated,omitempty"`
|
|
Explode *bool `json:"explode,omitempty"`
|
|
CLIName string `json:"x-cli-name,omitempty"`
|
|
|
|
// Internal params are excluded from the OpenAPI document and can set up
|
|
// params sent between a load balander / proxy and the service internally.
|
|
Internal bool `json:"-"`
|
|
|
|
typ reflect.Type
|
|
}
|
|
|
|
type oaComponents struct {
|
|
Schemas map[string]*schema.Schema `json:"schemas,omitempty"`
|
|
SecuritySchemes map[string]oaSecurityScheme `json:"securitySchemes,omitempty"`
|
|
}
|
|
|
|
// AddSchema creates and adds a new schema from a type.
|
|
func (c *oaComponents) AddSchema(t reflect.Type, mode schema.Mode, hint string, generateSchemaField bool) string {
|
|
return c.addSchema(t, mode, hint, generateSchemaField)
|
|
}
|
|
|
|
func (c *oaComponents) addSchema(t reflect.Type, mode schema.Mode, hint string, generateSchemaField bool) string {
|
|
// Try to determine the type's name.
|
|
name := t.Name()
|
|
if name == "" && t.Kind() == reflect.Ptr {
|
|
// Take the name of the pointed-to type.
|
|
name = t.Elem().Name()
|
|
}
|
|
if name == "" && t.Kind() == reflect.Slice {
|
|
// Take the name of the type in the array and append "List" to it.
|
|
tmp := t.Elem()
|
|
if tmp.Kind() == reflect.Ptr {
|
|
tmp = tmp.Elem()
|
|
}
|
|
name = tmp.Name()
|
|
if name != "" {
|
|
name += "List"
|
|
}
|
|
}
|
|
if name == "" {
|
|
// No luck, fall back to the passed-in hint. Better than nothing.
|
|
name = hint
|
|
}
|
|
|
|
var s *schema.Schema
|
|
|
|
if t.Kind() == reflect.Slice {
|
|
// We actually want to create two models: one for the container slice
|
|
// and one for the items within it.
|
|
ref := c.addSchema(t.Elem(), mode, name+"Item", false)
|
|
s = &schema.Schema{
|
|
Type: schema.TypeArray,
|
|
Items: &schema.Schema{
|
|
Ref: ref,
|
|
},
|
|
}
|
|
} else {
|
|
var err error
|
|
if s, err = schema.GenerateWithMode(t, mode, nil); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
return c.addExistingSchema(s, name, generateSchemaField)
|
|
}
|
|
|
|
//AddExistingSchema adds an existing schema instance under the given name.
|
|
func (c *oaComponents) AddExistingSchema(s *schema.Schema, name string, generateSchemaField bool) string {
|
|
return c.addExistingSchema(s, name, generateSchemaField)
|
|
}
|
|
|
|
func (c *oaComponents) addExistingSchema(s *schema.Schema, name string, generateSchemaField bool) string {
|
|
if generateSchemaField && s.Type == schema.TypeObject && s.Properties != nil {
|
|
if s.Properties["$schema"] == nil {
|
|
// Some editors allow you to place a $schema key which gives you rich
|
|
// validation and code completion support. Let's enable that by allowing
|
|
// a field here if it doesn't already exist in the model.
|
|
s.Properties["$schema"] = &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Format: "uri",
|
|
Description: "An optional URL to a JSON Schema document describing this resource",
|
|
}
|
|
}
|
|
}
|
|
|
|
orig := name
|
|
num := 1
|
|
for {
|
|
if c.Schemas[name] == nil {
|
|
// No existing schema, we are the first!
|
|
break
|
|
}
|
|
|
|
if reflect.DeepEqual(c.Schemas[name], s) {
|
|
// Existing schema matches!
|
|
break
|
|
}
|
|
|
|
// If we are here, then an existing schema doesn't match and this is a new
|
|
// type. So we will rename it in a deterministic fashion.
|
|
num++
|
|
name = fmt.Sprintf("%s%d", orig, num)
|
|
}
|
|
|
|
c.Schemas[name] = s
|
|
|
|
return "#/components/schemas/" + name
|
|
}
|
|
|
|
type oaFlow struct {
|
|
AuthorizationURL string `json:"authorizationUrl,omitempty"`
|
|
TokenURL string `json:"tokenUrl,omitempty"`
|
|
Scopes map[string]string `json:"scopes,omitempty"`
|
|
}
|
|
|
|
type oaFlows struct {
|
|
ClientCredentials *oaFlow `json:"clientCredentials,omitempty"`
|
|
AuthorizationCode *oaFlow `json:"authorizationCode,omitempty"`
|
|
}
|
|
|
|
type oaSecurityScheme struct {
|
|
Type string `json:"type"`
|
|
Scheme string `json:"scheme,omitempty"`
|
|
Flows oaFlows `json:"flows,omitempty"`
|
|
}
|