| /* |
| Copyright 2017 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package proto |
| |
| import ( |
| "fmt" |
| "sort" |
| "strings" |
| ) |
| |
| // Defines openapi types. |
| const ( |
| Integer = "integer" |
| Number = "number" |
| String = "string" |
| Boolean = "boolean" |
| |
| // These types are private as they should never leak, and are |
| // represented by actual structs. |
| array = "array" |
| object = "object" |
| ) |
| |
| // Models interface describe a model provider. They can give you the |
| // schema for a specific model. |
| type Models interface { |
| LookupModel(string) Schema |
| ListModels() []string |
| } |
| |
| // SchemaVisitor is an interface that you need to implement if you want |
| // to "visit" an openapi schema. A dispatch on the Schema type will call |
| // the appropriate function based on its actual type: |
| // - Array is a list of one and only one given subtype |
| // - Map is a map of string to one and only one given subtype |
| // - Primitive can be string, integer, number and boolean. |
| // - Kind is an object with specific fields mapping to specific types. |
| // - Reference is a link to another definition. |
| type SchemaVisitor interface { |
| VisitArray(*Array) |
| VisitMap(*Map) |
| VisitPrimitive(*Primitive) |
| VisitKind(*Kind) |
| VisitReference(Reference) |
| } |
| |
| // SchemaVisitorArbitrary is an additional visitor interface which handles |
| // arbitrary types. For backwards compatibility, it's a separate interface |
| // which is checked for at runtime. |
| type SchemaVisitorArbitrary interface { |
| SchemaVisitor |
| VisitArbitrary(*Arbitrary) |
| } |
| |
| // Schema is the base definition of an openapi type. |
| type Schema interface { |
| // Giving a visitor here will let you visit the actual type. |
| Accept(SchemaVisitor) |
| |
| // Pretty print the name of the type. |
| GetName() string |
| // Describes how to access this field. |
| GetPath() *Path |
| // Describes the field. |
| GetDescription() string |
| // Returns type extensions. |
| GetExtensions() map[string]interface{} |
| } |
| |
| // Path helps us keep track of type paths |
| type Path struct { |
| parent *Path |
| key string |
| } |
| |
| func NewPath(key string) Path { |
| return Path{key: key} |
| } |
| |
| func (p *Path) Get() []string { |
| if p == nil { |
| return []string{} |
| } |
| if p.key == "" { |
| return p.parent.Get() |
| } |
| return append(p.parent.Get(), p.key) |
| } |
| |
| func (p *Path) Len() int { |
| return len(p.Get()) |
| } |
| |
| func (p *Path) String() string { |
| return strings.Join(p.Get(), "") |
| } |
| |
| // ArrayPath appends an array index and creates a new path |
| func (p *Path) ArrayPath(i int) Path { |
| return Path{ |
| parent: p, |
| key: fmt.Sprintf("[%d]", i), |
| } |
| } |
| |
| // FieldPath appends a field name and creates a new path |
| func (p *Path) FieldPath(field string) Path { |
| return Path{ |
| parent: p, |
| key: fmt.Sprintf(".%s", field), |
| } |
| } |
| |
| // BaseSchema holds data used by each types of schema. |
| type BaseSchema struct { |
| Description string |
| Extensions map[string]interface{} |
| |
| Path Path |
| } |
| |
| func (b *BaseSchema) GetDescription() string { |
| return b.Description |
| } |
| |
| func (b *BaseSchema) GetExtensions() map[string]interface{} { |
| return b.Extensions |
| } |
| |
| func (b *BaseSchema) GetPath() *Path { |
| return &b.Path |
| } |
| |
| // Array must have all its element of the same `SubType`. |
| type Array struct { |
| BaseSchema |
| |
| SubType Schema |
| } |
| |
| var _ Schema = &Array{} |
| |
| func (a *Array) Accept(v SchemaVisitor) { |
| v.VisitArray(a) |
| } |
| |
| func (a *Array) GetName() string { |
| return fmt.Sprintf("Array of %s", a.SubType.GetName()) |
| } |
| |
| // Kind is a complex object. It can have multiple different |
| // subtypes for each field, as defined in the `Fields` field. Mandatory |
| // fields are listed in `RequiredFields`. The key of the object is |
| // always of type `string`. |
| type Kind struct { |
| BaseSchema |
| |
| // Lists names of required fields. |
| RequiredFields []string |
| // Maps field names to types. |
| Fields map[string]Schema |
| // FieldOrder reports the canonical order for the fields. |
| FieldOrder []string |
| } |
| |
| var _ Schema = &Kind{} |
| |
| func (k *Kind) Accept(v SchemaVisitor) { |
| v.VisitKind(k) |
| } |
| |
| func (k *Kind) GetName() string { |
| properties := []string{} |
| for key := range k.Fields { |
| properties = append(properties, key) |
| } |
| return fmt.Sprintf("Kind(%v)", properties) |
| } |
| |
| // IsRequired returns true if `field` is a required field for this type. |
| func (k *Kind) IsRequired(field string) bool { |
| for _, f := range k.RequiredFields { |
| if f == field { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // Keys returns a alphabetically sorted list of keys. |
| func (k *Kind) Keys() []string { |
| keys := make([]string, 0) |
| for key := range k.Fields { |
| keys = append(keys, key) |
| } |
| sort.Strings(keys) |
| return keys |
| } |
| |
| // Map is an object who values must all be of the same `SubType`. |
| // The key of the object is always of type `string`. |
| type Map struct { |
| BaseSchema |
| |
| SubType Schema |
| } |
| |
| var _ Schema = &Map{} |
| |
| func (m *Map) Accept(v SchemaVisitor) { |
| v.VisitMap(m) |
| } |
| |
| func (m *Map) GetName() string { |
| return fmt.Sprintf("Map of %s", m.SubType.GetName()) |
| } |
| |
| // Primitive is a literal. There can be multiple types of primitives, |
| // and this subtype can be visited through the `subType` field. |
| type Primitive struct { |
| BaseSchema |
| |
| // Type of a primitive must be one of: integer, number, string, boolean. |
| Type string |
| Format string |
| } |
| |
| var _ Schema = &Primitive{} |
| |
| func (p *Primitive) Accept(v SchemaVisitor) { |
| v.VisitPrimitive(p) |
| } |
| |
| func (p *Primitive) GetName() string { |
| if p.Format == "" { |
| return p.Type |
| } |
| return fmt.Sprintf("%s (%s)", p.Type, p.Format) |
| } |
| |
| // Arbitrary is a value of any type (primitive, object or array) |
| type Arbitrary struct { |
| BaseSchema |
| } |
| |
| var _ Schema = &Arbitrary{} |
| |
| func (a *Arbitrary) Accept(v SchemaVisitor) { |
| if visitor, ok := v.(SchemaVisitorArbitrary); ok { |
| visitor.VisitArbitrary(a) |
| } |
| } |
| |
| func (a *Arbitrary) GetName() string { |
| return "Arbitrary value (primitive, object or array)" |
| } |
| |
| // Reference implementation depends on the type of document. |
| type Reference interface { |
| Schema |
| |
| Reference() string |
| SubSchema() Schema |
| } |