k3s/vendor/github.com/rancher/wrangler/pkg/schemas/openapi/generate.go

220 lines
4.9 KiB
Go
Raw Normal View History

2020-03-26 21:07:15 +00:00
package openapi
import (
2020-04-22 22:34:19 +00:00
"encoding/json"
2020-03-26 21:07:15 +00:00
"fmt"
types "github.com/rancher/wrangler/pkg/schemas"
"github.com/rancher/wrangler/pkg/schemas/definition"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
)
2020-04-22 22:34:19 +00:00
var (
blacklistFields = map[string]bool{
"kind": true,
"apiVersion": true,
"metadata": true,
}
)
2020-03-26 21:07:15 +00:00
func MustGenerate(obj interface{}) *v1beta1.JSONSchemaProps {
if obj == nil {
return nil
}
result, err := ToOpenAPIFromStruct(obj)
if err != nil {
panic(err)
}
return result
}
func ToOpenAPIFromStruct(obj interface{}) (*v1beta1.JSONSchemaProps, error) {
schemas := types.EmptySchemas()
schema, err := schemas.Import(obj)
if err != nil {
return nil, err
}
return toOpenAPI(schema.ID, schemas)
}
func toOpenAPI(name string, schemas *types.Schemas) (*v1beta1.JSONSchemaProps, error) {
schema := schemas.Schema(name)
if schema == nil {
return nil, fmt.Errorf("failed to find schema: %s", name)
}
newSchema := schema.DeepCopy()
if newSchema.InternalSchema != nil {
newSchema = newSchema.InternalSchema.DeepCopy()
}
delete(newSchema.ResourceFields, "kind")
delete(newSchema.ResourceFields, "apiVersion")
delete(newSchema.ResourceFields, "metadata")
2020-04-22 22:34:19 +00:00
return schemaToProps(newSchema, schemas, map[string]bool{})
2020-03-26 21:07:15 +00:00
}
2020-04-22 22:34:19 +00:00
func populateField(fieldJSP *v1beta1.JSONSchemaProps, f *types.Field) error {
fieldJSP.Description = f.Description
// don't reset this to not nullable
if f.Nullable {
fieldJSP.Nullable = f.Nullable
2020-03-26 21:07:15 +00:00
}
2020-04-22 22:34:19 +00:00
fieldJSP.MinLength = f.MinLength
fieldJSP.MaxLength = f.MaxLength
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
if f.Type == "string" && len(f.Options) > 0 {
for _, opt := range append(f.Options, "") {
bytes, err := json.Marshal(&opt)
if err != nil {
return err
2020-03-26 21:07:15 +00:00
}
2020-04-22 22:34:19 +00:00
fieldJSP.Enum = append(fieldJSP.Enum, v1beta1.JSON{
Raw: bytes,
})
2020-03-26 21:07:15 +00:00
}
2020-04-22 22:34:19 +00:00
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
if len(f.InvalidChars) > 0 {
fieldJSP.Pattern = fmt.Sprintf("^[^%s]*$", f.InvalidChars)
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
if len(f.ValidChars) > 0 {
fieldJSP.Pattern = fmt.Sprintf("^[%s]*$", f.ValidChars)
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
if f.Min != nil {
fl := float64(*f.Min)
fieldJSP.Minimum = &fl
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
if f.Max != nil {
fl := float64(*f.Max)
fieldJSP.Maximum = &fl
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
return nil
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
func typeToProps(typeName string, schemas *types.Schemas, inflight map[string]bool) (*v1beta1.JSONSchemaProps, error) {
t, subType, schema, err := typeAndSchema(typeName, schemas)
if err != nil {
return nil, err
}
if schema != nil {
return schemaToProps(schema, schemas, inflight)
}
jsp := &v1beta1.JSONSchemaProps{}
switch t {
case "map":
additionalProps, err := typeToProps(subType, schemas, inflight)
if err != nil {
return nil, err
}
jsp.Type = "object"
jsp.Nullable = true
jsp.AdditionalProperties = &v1beta1.JSONSchemaPropsOrBool{
Schema: additionalProps,
}
case "array":
items, err := typeToProps(subType, schemas, inflight)
if err != nil {
return nil, err
2020-03-26 21:07:15 +00:00
}
2020-04-22 22:34:19 +00:00
jsp.Type = "array"
jsp.Nullable = true
jsp.Items = &v1beta1.JSONSchemaPropsOrArray{
Schema: items,
}
default:
jsp.Type = t
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
return jsp, nil
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
func schemaToProps(schema *types.Schema, schemas *types.Schemas, inflight map[string]bool) (*v1beta1.JSONSchemaProps, error) {
jsp := &v1beta1.JSONSchemaProps{
Description: schema.Description,
Type: "object",
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
if inflight[schema.ID] {
return jsp, nil
}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
inflight[schema.ID] = true
defer delete(inflight, schema.ID)
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
jsp.Properties = map[string]v1beta1.JSONSchemaProps{}
2020-03-26 21:07:15 +00:00
2020-04-22 22:34:19 +00:00
for name, f := range schema.ResourceFields {
fieldJSP, err := typeToProps(f.Type, schemas, inflight)
if err != nil {
return nil, err
}
if err := populateField(fieldJSP, &f); err != nil {
return nil, err
}
if f.Required {
jsp.Required = append(jsp.Required, name)
}
jsp.Properties[name] = *fieldJSP
2020-03-26 21:07:15 +00:00
}
return jsp, nil
}
2020-04-22 22:34:19 +00:00
func typeAndSchema(typeName string, schemas *types.Schemas) (string, string, *types.Schema, error) {
if definition.IsReferenceType(typeName) {
return "string", "", nil, nil
}
if definition.IsArrayType(typeName) {
return "array", definition.SubType(typeName), nil, nil
}
if definition.IsMapType(typeName) {
return "map", definition.SubType(typeName), nil, nil
}
2020-03-26 21:07:15 +00:00
switch typeName {
// TODO: in v1 set the x- header for this
case "intOrString":
2020-04-22 22:34:19 +00:00
return "string", "", nil, nil
2020-03-26 21:07:15 +00:00
case "int":
2020-04-22 22:34:19 +00:00
return "integer", "", nil, nil
2020-03-26 21:07:15 +00:00
case "float":
2020-04-22 22:34:19 +00:00
return "number", "", nil, nil
2020-03-26 21:07:15 +00:00
case "string":
2020-04-22 22:34:19 +00:00
return "string", "", nil, nil
2020-03-26 21:07:15 +00:00
case "date":
2020-04-22 22:34:19 +00:00
return "string", "", nil, nil
2020-03-26 21:07:15 +00:00
case "enum":
2020-04-22 22:34:19 +00:00
return "string", "", nil, nil
case "base64":
return "string", "", nil, nil
2020-03-26 21:07:15 +00:00
case "password":
2020-04-22 22:34:19 +00:00
return "string", "", nil, nil
2020-03-26 21:07:15 +00:00
case "hostname":
2020-04-22 22:34:19 +00:00
return "string", "", nil, nil
2020-03-26 21:07:15 +00:00
case "boolean":
2020-04-22 22:34:19 +00:00
return "boolean", "", nil, nil
2020-03-26 21:07:15 +00:00
case "json":
2020-04-22 22:34:19 +00:00
return "object", "", nil, nil
2020-03-26 21:07:15 +00:00
}
schema := schemas.Schema(typeName)
if schema == nil {
2020-04-22 22:34:19 +00:00
return "", "", nil, fmt.Errorf("failed to find schema %s", typeName)
2020-03-26 21:07:15 +00:00
}
if schema.InternalSchema != nil {
2020-04-22 22:34:19 +00:00
return "", "", schema.InternalSchema, nil
2020-03-26 21:07:15 +00:00
}
2020-04-22 22:34:19 +00:00
return "", "", schema, nil
2020-03-26 21:07:15 +00:00
}