diff --git a/autoconfig.go b/autoconfig.go index f0c7f750..f354bcb8 100644 --- a/autoconfig.go +++ b/autoconfig.go @@ -7,6 +7,10 @@ type AutoConfigVar struct { Example string `json:"example,omitempty"` Default interface{} `json:"default,omitempty"` Enum []interface{} `json:"enum,omitempty"` + + // Exclude the value from being sent to the server. This essentially makes + // it a value which is only used in param templates. + Exclude bool `json:"exclude,omitempty"` } // AutoConfig holds an API's automatic configuration settings for the CLI. These diff --git a/openapi.go b/openapi.go index ab0f9f74..be6c3e09 100644 --- a/openapi.go +++ b/openapi.go @@ -73,9 +73,23 @@ func (c *oaComponents) AddSchema(t reflect.Type, mode schema.Mode, hint string) name = hint } - s, err := schema.GenerateWithMode(t, mode, nil) - if err != nil { - panic(err) + 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") + 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) + } } orig := name diff --git a/operation.go b/operation.go index 8b4b3546..44842131 100644 --- a/operation.go +++ b/operation.go @@ -22,6 +22,7 @@ type Operation struct { params map[string]oaParam requestContentType string requestSchema *schema.Schema + requestModel reflect.Type responses []Response maxBodyBytes int64 bodyReadTimeout time.Duration @@ -70,7 +71,8 @@ func (o *Operation) toOpenAPI(components *oaComponents) *gabs.Container { if ct == "" { ct = "application/json" } - doc.Set(o.requestSchema, "requestBody", "content", ct, "schema") + ref := components.AddSchema(o.requestModel, schema.ModeAll, o.id+"-request") + doc.Set(ref, "requestBody", "content", ct, "schema", "$ref") } // responses @@ -98,7 +100,7 @@ func (o *Operation) toOpenAPI(components *oaComponents) *gabs.Container { } if resp.model != nil { - ref := components.AddSchema(resp.model, schema.ModeRead, o.id) + ref := components.AddSchema(resp.model, schema.ModeAll, o.id+"-response") doc.Set(ref, "responses", status, "content", resp.contentType, "schema", "$ref") } } @@ -179,6 +181,7 @@ func (o *Operation) Run(handler interface{}) { // Get body if present. if body, ok := input.FieldByName("Body"); ok { + o.requestModel = body.Type o.requestSchema, err = schema.GenerateWithMode(body.Type, schema.ModeWrite, nil) if err != nil { panic(fmt.Errorf("unable to generate JSON schema: %w", err)) diff --git a/router.go b/router.go index 4aa40a16..481c332f 100644 --- a/router.go +++ b/router.go @@ -73,6 +73,10 @@ func (r *Router) OpenAPI() *gabs.Container { doc.Set(r.description, "info", "description") } + if len(r.servers) > 0 { + doc.Set(r.servers, "servers") + } + components := &oaComponents{ Schemas: map[string]*schema.Schema{}, SecuritySchemes: r.securitySchemes, diff --git a/schema/schema.go b/schema/schema.go index 0e22e6e6..39961b54 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -144,13 +144,14 @@ type Schema struct { WriteOnly bool `json:"writeOnly,omitempty"` Deprecated bool `json:"deprecated,omitempty"` ContentEncoding string `json:"contentEncoding,omitempty"` + Ref string `json:"$ref,omitempty"` } // HasValidation returns true if at least one validator is set on the schema. // This excludes the schema's type but includes most other fields and can be // used to trigger additional slow validation steps when needed. func (s *Schema) HasValidation() bool { - if s.Items != nil || len(s.Properties) > 0 || s.AdditionalProperties != nil || len(s.PatternProperties) > 0 || len(s.Required) > 0 || len(s.Enum) > 0 || s.Minimum != nil || s.ExclusiveMinimum != nil || s.Maximum != nil || s.ExclusiveMaximum != nil || s.MultipleOf != 0 || s.MinLength != nil || s.MaxLength != nil || s.Pattern != "" || s.MinItems != nil || s.MaxItems != nil || s.UniqueItems || s.MinProperties != nil || s.MaxProperties != nil || len(s.AllOf) > 0 || len(s.AnyOf) > 0 || len(s.OneOf) > 0 || s.Not != nil { + if s.Items != nil || len(s.Properties) > 0 || s.AdditionalProperties != nil || len(s.PatternProperties) > 0 || len(s.Required) > 0 || len(s.Enum) > 0 || s.Minimum != nil || s.ExclusiveMinimum != nil || s.Maximum != nil || s.ExclusiveMaximum != nil || s.MultipleOf != 0 || s.MinLength != nil || s.MaxLength != nil || s.Pattern != "" || s.MinItems != nil || s.MaxItems != nil || s.UniqueItems || s.MinProperties != nil || s.MaxProperties != nil || len(s.AllOf) > 0 || len(s.AnyOf) > 0 || len(s.OneOf) > 0 || s.Not != nil || s.Ref != "" { return true }