Add OpenAPI support for query parameters (#6490)
Also, fix handling of required properties in request body.
This commit is contained in:
parent
d14a2d326d
commit
265e61b993
|
@ -506,6 +506,13 @@ type FieldSchema struct {
|
|||
Required bool
|
||||
Deprecated bool
|
||||
|
||||
// Query indicates this field will be sent as a query parameter:
|
||||
//
|
||||
// /v1/foo/bar?some_param=some_value
|
||||
//
|
||||
// It doesn't affect handling of the value, but may be used for documentation.
|
||||
Query bool
|
||||
|
||||
// AllowedValues is an optional list of permitted values for this field.
|
||||
// This constraint is not (yet) enforced by the framework, but the list is
|
||||
// output as part of OpenAPI generation and may effect documentation and
|
||||
|
|
|
@ -148,20 +148,24 @@ type OASMediaTypeObject struct {
|
|||
}
|
||||
|
||||
type OASSchema struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Properties map[string]*OASSchema `json:"properties,omitempty"`
|
||||
Items *OASSchema `json:"items,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
Enum []interface{} `json:"enum,omitempty"`
|
||||
Default interface{} `json:"default,omitempty"`
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty"`
|
||||
Required bool `json:"required,omitempty"`
|
||||
DisplayName string `json:"x-vault-displayName,omitempty" mapstructure:"x-vault-displayName,omitempty"`
|
||||
DisplayValue interface{} `json:"x-vault-displayValue,omitempty" mapstructure:"x-vault-displayValue,omitempty"`
|
||||
DisplaySensitive bool `json:"x-vault-displaySensitive,omitempty" mapstructure:"x-vault-displaySensitive,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Properties map[string]*OASSchema `json:"properties,omitempty"`
|
||||
|
||||
// Required is a list of keys in Properties that are required to be present. This is a different
|
||||
// approach than OASParameter (unfortunately), but is how JSONSchema handles 'required'.
|
||||
Required []string `json:"required,omitempty"`
|
||||
|
||||
Items *OASSchema `json:"items,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
Enum []interface{} `json:"enum,omitempty"`
|
||||
Default interface{} `json:"default,omitempty"`
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty"`
|
||||
DisplayName string `json:"x-vault-displayName,omitempty" mapstructure:"x-vault-displayName,omitempty"`
|
||||
DisplayValue interface{} `json:"x-vault-displayValue,omitempty" mapstructure:"x-vault-displayValue,omitempty"`
|
||||
DisplaySensitive bool `json:"x-vault-displaySensitive,omitempty" mapstructure:"x-vault-displaySensitive,omitempty"`
|
||||
}
|
||||
|
||||
type OASResponse struct {
|
||||
|
@ -248,6 +252,11 @@ func documentPath(p *Path, specialPaths *logical.Paths, backendType logical.Back
|
|||
location := "path"
|
||||
required := true
|
||||
|
||||
if field.Query {
|
||||
location = "query"
|
||||
required = false
|
||||
}
|
||||
|
||||
// Header parameters are part of the Parameters group but with
|
||||
// a dedicated "header" location, a header parameter is not required.
|
||||
if field.Type == TypeHeader {
|
||||
|
@ -313,10 +322,15 @@ func documentPath(p *Path, specialPaths *logical.Paths, backendType logical.Back
|
|||
s := &OASSchema{
|
||||
Type: "object",
|
||||
Properties: make(map[string]*OASSchema),
|
||||
Required: make([]string, 0),
|
||||
}
|
||||
|
||||
for name, field := range bodyFields {
|
||||
openapiField := convertType(field.Type)
|
||||
if field.Required {
|
||||
s.Required = append(s.Required, name)
|
||||
}
|
||||
|
||||
p := OASSchema{
|
||||
Type: openapiField.baseType,
|
||||
Description: cleanString(field.Description),
|
||||
|
@ -324,7 +338,6 @@ func documentPath(p *Path, specialPaths *logical.Paths, backendType logical.Back
|
|||
Pattern: openapiField.pattern,
|
||||
Enum: field.AllowedValues,
|
||||
Default: field.Default,
|
||||
Required: field.Required,
|
||||
Deprecated: field.Deprecated,
|
||||
DisplayName: field.DisplayName,
|
||||
DisplayValue: field.DisplayValue,
|
||||
|
@ -596,7 +609,7 @@ func splitFields(allFields map[string]*FieldSchema, pattern string) (pathFields,
|
|||
for name, field := range allFields {
|
||||
if _, ok := pathFields[name]; !ok {
|
||||
// Header fields are in "parameters" with other path fields
|
||||
if field.Type == TypeHeader {
|
||||
if field.Type == TypeHeader || field.Query {
|
||||
pathFields[name] = field
|
||||
} else {
|
||||
bodyFields[name] = field
|
||||
|
|
|
@ -345,6 +345,11 @@ func TestOpenAPI_Paths(t *testing.T) {
|
|||
Description: "a header value",
|
||||
AllowedValues: []interface{}{"a", "b", "c"},
|
||||
},
|
||||
"format": {
|
||||
Type: TypeString,
|
||||
Description: "a query param",
|
||||
Query: true,
|
||||
},
|
||||
},
|
||||
HelpSynopsis: "Synopsis",
|
||||
HelpDescription: "Description",
|
||||
|
@ -555,7 +560,9 @@ func testPath(t *testing.T, path *Path, sp *logical.Paths, expectedJSON string)
|
|||
t.Helper()
|
||||
|
||||
doc := NewOASDocument()
|
||||
documentPath(path, sp, logical.TypeLogical, doc)
|
||||
if err := documentPath(path, sp, logical.TypeLogical, doc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
doc.CreateOperationIDs("")
|
||||
|
||||
docJSON, err := json.MarshalIndent(doc, "", " ")
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
"x-vault-createSupported": true,
|
||||
"x-vault-sudo": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "format",
|
||||
"description": "a query param",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "id path parameter",
|
||||
|
@ -65,6 +73,7 @@
|
|||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["age"],
|
||||
"properties": {
|
||||
"flavors": {
|
||||
"type": "array",
|
||||
|
@ -77,7 +86,6 @@
|
|||
"type": "integer",
|
||||
"description": "the age",
|
||||
"enum": [1, 2, 3],
|
||||
"required": true,
|
||||
"x-vault-displayName": "Age",
|
||||
"x-vault-displayValue": 7,
|
||||
"x-vault-displaySensitive": true
|
||||
|
|
|
@ -1117,7 +1117,8 @@ func (b *SystemBackend) metricsPath() *framework.Path {
|
|||
Fields: map[string]*framework.FieldSchema{
|
||||
"format": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Format to export metrics into. Currently accept only \"prometheus\"",
|
||||
Description: "Format to export metrics into. Currently accepts only \"prometheus\".",
|
||||
Query: true,
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
|
Loading…
Reference in New Issue