blob: f26b5ef88104143e7099beef6412b18f06094211 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
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 // 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}