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 compatibility, 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 // Default for that schema. 81 GetDefault() interface{} 82 // Returns type extensions. 83 GetExtensions() map[string]interface{} 84} 85 86// Path helps us keep track of type paths 87type Path struct { 88 parent *Path 89 key string 90} 91 92func NewPath(key string) Path { 93 return Path{key: key} 94} 95 96func (p *Path) Get() []string { 97 if p == nil { 98 return []string{} 99 } 100 if p.key == "" { 101 return p.parent.Get() 102 } 103 return append(p.parent.Get(), p.key) 104} 105 106func (p *Path) Len() int { 107 return len(p.Get()) 108} 109 110func (p *Path) String() string { 111 return strings.Join(p.Get(), "") 112} 113 114// ArrayPath appends an array index and creates a new path 115func (p *Path) ArrayPath(i int) Path { 116 return Path{ 117 parent: p, 118 key: fmt.Sprintf("[%d]", i), 119 } 120} 121 122// FieldPath appends a field name and creates a new path 123func (p *Path) FieldPath(field string) Path { 124 return Path{ 125 parent: p, 126 key: fmt.Sprintf(".%s", field), 127 } 128} 129 130// BaseSchema holds data used by each types of schema. 131type BaseSchema struct { 132 Description string 133 Extensions map[string]interface{} 134 Default interface{} 135 136 Path Path 137} 138 139func (b *BaseSchema) GetDescription() string { 140 return b.Description 141} 142 143func (b *BaseSchema) GetExtensions() map[string]interface{} { 144 return b.Extensions 145} 146 147func (b *BaseSchema) GetDefault() interface{} { 148 return b.Default 149} 150 151func (b *BaseSchema) GetPath() *Path { 152 return &b.Path 153} 154 155// Array must have all its element of the same `SubType`. 156type Array struct { 157 BaseSchema 158 159 SubType Schema 160} 161 162var _ Schema = &Array{} 163 164func (a *Array) Accept(v SchemaVisitor) { 165 v.VisitArray(a) 166} 167 168func (a *Array) GetName() string { 169 return fmt.Sprintf("Array of %s", a.SubType.GetName()) 170} 171 172// Kind is a complex object. It can have multiple different 173// subtypes for each field, as defined in the `Fields` field. Mandatory 174// fields are listed in `RequiredFields`. The key of the object is 175// always of type `string`. 176type Kind struct { 177 BaseSchema 178 179 // Lists names of required fields. 180 RequiredFields []string 181 // Maps field names to types. 182 Fields map[string]Schema 183 // FieldOrder reports the canonical order for the fields. 184 FieldOrder []string 185} 186 187var _ Schema = &Kind{} 188 189func (k *Kind) Accept(v SchemaVisitor) { 190 v.VisitKind(k) 191} 192 193func (k *Kind) GetName() string { 194 properties := []string{} 195 for key := range k.Fields { 196 properties = append(properties, key) 197 } 198 return fmt.Sprintf("Kind(%v)", properties) 199} 200 201// IsRequired returns true if `field` is a required field for this type. 202func (k *Kind) IsRequired(field string) bool { 203 for _, f := range k.RequiredFields { 204 if f == field { 205 return true 206 } 207 } 208 return false 209} 210 211// Keys returns a alphabetically sorted list of keys. 212func (k *Kind) Keys() []string { 213 keys := make([]string, 0) 214 for key := range k.Fields { 215 keys = append(keys, key) 216 } 217 sort.Strings(keys) 218 return keys 219} 220 221// Map is an object who values must all be of the same `SubType`. 222// The key of the object is always of type `string`. 223type Map struct { 224 BaseSchema 225 226 SubType Schema 227} 228 229var _ Schema = &Map{} 230 231func (m *Map) Accept(v SchemaVisitor) { 232 v.VisitMap(m) 233} 234 235func (m *Map) GetName() string { 236 return fmt.Sprintf("Map of %s", m.SubType.GetName()) 237} 238 239// Primitive is a literal. There can be multiple types of primitives, 240// and this subtype can be visited through the `subType` field. 241type Primitive struct { 242 BaseSchema 243 244 // Type of a primitive must be one of: integer, number, string, boolean. 245 Type string 246 Format string 247} 248 249var _ Schema = &Primitive{} 250 251func (p *Primitive) Accept(v SchemaVisitor) { 252 v.VisitPrimitive(p) 253} 254 255func (p *Primitive) GetName() string { 256 if p.Format == "" { 257 return p.Type 258 } 259 return fmt.Sprintf("%s (%s)", p.Type, p.Format) 260} 261 262// Arbitrary is a value of any type (primitive, object or array) 263type Arbitrary struct { 264 BaseSchema 265} 266 267var _ Schema = &Arbitrary{} 268 269func (a *Arbitrary) Accept(v SchemaVisitor) { 270 if visitor, ok := v.(SchemaVisitorArbitrary); ok { 271 visitor.VisitArbitrary(a) 272 } 273} 274 275func (a *Arbitrary) GetName() string { 276 return "Arbitrary value (primitive, object or array)" 277} 278 279// Reference implementation depends on the type of document. 280type Reference interface { 281 Schema 282 283 Reference() string 284 SubSchema() Schema 285} 286