1/* 2Copyright 2017 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package proto 18 19import ( 20 "fmt" 21 "sort" 22 "strings" 23) 24 25// Defines openapi types. 26const ( 27 Integer = "integer" 28 Number = "number" 29 String = "string" 30 Boolean = "boolean" 31 32 // These types are private as they should never leak, and are 33 // represented by actual structs. 34 array = "array" 35 object = "object" 36) 37 38// Models interface describe a model provider. They can give you the 39// schema for a specific model. 40type Models interface { 41 LookupModel(string) Schema 42 ListModels() []string 43} 44 45// SchemaVisitor is an interface that you need to implement if you want 46// to "visit" an openapi schema. A dispatch on the Schema type will call 47// the appropriate function based on its actual type: 48// - Array is a list of one and only one given subtype 49// - Map is a map of string to one and only one given subtype 50// - Primitive can be string, integer, number and boolean. 51// - Kind is an object with specific fields mapping to specific types. 52// - Reference is a link to another definition. 53type SchemaVisitor interface { 54 VisitArray(*Array) 55 VisitMap(*Map) 56 VisitPrimitive(*Primitive) 57 VisitKind(*Kind) 58 VisitReference(Reference) 59} 60 61// SchemaVisitorArbitrary is an additional visitor interface which handles 62// arbitrary types. For backwards compatability, it's a separate interface 63// which is checked for at runtime. 64type SchemaVisitorArbitrary interface { 65 SchemaVisitor 66 VisitArbitrary(*Arbitrary) 67} 68 69// Schema is the base definition of an openapi type. 70type Schema interface { 71 // Giving a visitor here will let you visit the actual type. 72 Accept(SchemaVisitor) 73 74 // Pretty print the name of the type. 75 GetName() string 76 // Describes how to access this field. 77 GetPath() *Path 78 // Describes the field. 79 GetDescription() string 80 // Returns type extensions. 81 GetExtensions() map[string]interface{} 82} 83 84// Path helps us keep track of type paths 85type Path struct { 86 parent *Path 87 key string 88} 89 90func NewPath(key string) Path { 91 return Path{key: key} 92} 93 94func (p *Path) Get() []string { 95 if p == nil { 96 return []string{} 97 } 98 if p.key == "" { 99 return p.parent.Get() 100 } 101 return append(p.parent.Get(), p.key) 102} 103 104func (p *Path) Len() int { 105 return len(p.Get()) 106} 107 108func (p *Path) String() string { 109 return strings.Join(p.Get(), "") 110} 111 112// ArrayPath appends an array index and creates a new path 113func (p *Path) ArrayPath(i int) Path { 114 return Path{ 115 parent: p, 116 key: fmt.Sprintf("[%d]", i), 117 } 118} 119 120// FieldPath appends a field name and creates a new path 121func (p *Path) FieldPath(field string) Path { 122 return Path{ 123 parent: p, 124 key: fmt.Sprintf(".%s", field), 125 } 126} 127 128// BaseSchema holds data used by each types of schema. 129type BaseSchema struct { 130 Description string 131 Extensions map[string]interface{} 132 133 Path Path 134} 135 136func (b *BaseSchema) GetDescription() string { 137 return b.Description 138} 139 140func (b *BaseSchema) GetExtensions() map[string]interface{} { 141 return b.Extensions 142} 143 144func (b *BaseSchema) GetPath() *Path { 145 return &b.Path 146} 147 148// Array must have all its element of the same `SubType`. 149type Array struct { 150 BaseSchema 151 152 SubType Schema 153} 154 155var _ Schema = &Array{} 156 157func (a *Array) Accept(v SchemaVisitor) { 158 v.VisitArray(a) 159} 160 161func (a *Array) GetName() string { 162 return fmt.Sprintf("Array of %s", a.SubType.GetName()) 163} 164 165// Kind is a complex object. It can have multiple different 166// subtypes for each field, as defined in the `Fields` field. Mandatory 167// fields are listed in `RequiredFields`. The key of the object is 168// always of type `string`. 169type Kind struct { 170 BaseSchema 171 172 // Lists names of required fields. 173 RequiredFields []string 174 // Maps field names to types. 175 Fields map[string]Schema 176} 177 178var _ Schema = &Kind{} 179 180func (k *Kind) Accept(v SchemaVisitor) { 181 v.VisitKind(k) 182} 183 184func (k *Kind) GetName() string { 185 properties := []string{} 186 for key := range k.Fields { 187 properties = append(properties, key) 188 } 189 return fmt.Sprintf("Kind(%v)", properties) 190} 191 192// IsRequired returns true if `field` is a required field for this type. 193func (k *Kind) IsRequired(field string) bool { 194 for _, f := range k.RequiredFields { 195 if f == field { 196 return true 197 } 198 } 199 return false 200} 201 202// Keys returns a alphabetically sorted list of keys. 203func (k *Kind) Keys() []string { 204 keys := make([]string, 0) 205 for key := range k.Fields { 206 keys = append(keys, key) 207 } 208 sort.Strings(keys) 209 return keys 210} 211 212// Map is an object who values must all be of the same `SubType`. 213// The key of the object is always of type `string`. 214type Map struct { 215 BaseSchema 216 217 SubType Schema 218} 219 220var _ Schema = &Map{} 221 222func (m *Map) Accept(v SchemaVisitor) { 223 v.VisitMap(m) 224} 225 226func (m *Map) GetName() string { 227 return fmt.Sprintf("Map of %s", m.SubType.GetName()) 228} 229 230// Primitive is a literal. There can be multiple types of primitives, 231// and this subtype can be visited through the `subType` field. 232type Primitive struct { 233 BaseSchema 234 235 // Type of a primitive must be one of: integer, number, string, boolean. 236 Type string 237 Format string 238} 239 240var _ Schema = &Primitive{} 241 242func (p *Primitive) Accept(v SchemaVisitor) { 243 v.VisitPrimitive(p) 244} 245 246func (p *Primitive) GetName() string { 247 if p.Format == "" { 248 return p.Type 249 } 250 return fmt.Sprintf("%s (%s)", p.Type, p.Format) 251} 252 253// Arbitrary is a value of any type (primitive, object or array) 254type Arbitrary struct { 255 BaseSchema 256} 257 258var _ Schema = &Arbitrary{} 259 260func (a *Arbitrary) Accept(v SchemaVisitor) { 261 if visitor, ok := v.(SchemaVisitorArbitrary); ok { 262 visitor.VisitArbitrary(a) 263 } 264} 265 266func (a *Arbitrary) GetName() string { 267 return "Arbitrary value (primitive, object or array)" 268} 269 270// Reference implementation depends on the type of document. 271type Reference interface { 272 Schema 273 274 Reference() string 275 SubSchema() Schema 276} 277