openapi.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package generators
  14. import (
  15. "bytes"
  16. "fmt"
  17. "io"
  18. "path/filepath"
  19. "reflect"
  20. "sort"
  21. "strings"
  22. "k8s.io/gengo/generator"
  23. "k8s.io/gengo/namer"
  24. "k8s.io/gengo/types"
  25. openapi "k8s.io/kube-openapi/pkg/common"
  26. "k8s.io/klog"
  27. )
  28. // This is the comment tag that carries parameters for open API generation.
  29. const tagName = "k8s:openapi-gen"
  30. const tagOptional = "optional"
  31. // Known values for the tag.
  32. const (
  33. tagValueTrue = "true"
  34. tagValueFalse = "false"
  35. )
  36. // Used for temporary validation of patch struct tags.
  37. // TODO: Remove patch struct tag validation because they we are now consuming OpenAPI on server.
  38. var tempPatchTags = [...]string{
  39. "patchMergeKey",
  40. "patchStrategy",
  41. }
  42. func getOpenAPITagValue(comments []string) []string {
  43. return types.ExtractCommentTags("+", comments)[tagName]
  44. }
  45. func getSingleTagsValue(comments []string, tag string) (string, error) {
  46. tags, ok := types.ExtractCommentTags("+", comments)[tag]
  47. if !ok || len(tags) == 0 {
  48. return "", nil
  49. }
  50. if len(tags) > 1 {
  51. return "", fmt.Errorf("multiple values are not allowed for tag %s", tag)
  52. }
  53. return tags[0], nil
  54. }
  55. func hasOpenAPITagValue(comments []string, value string) bool {
  56. tagValues := getOpenAPITagValue(comments)
  57. for _, val := range tagValues {
  58. if val == value {
  59. return true
  60. }
  61. }
  62. return false
  63. }
  64. // hasOptionalTag returns true if the member has +optional in its comments or
  65. // omitempty in its json tags.
  66. func hasOptionalTag(m *types.Member) bool {
  67. hasOptionalCommentTag := types.ExtractCommentTags(
  68. "+", m.CommentLines)[tagOptional] != nil
  69. hasOptionalJsonTag := strings.Contains(
  70. reflect.StructTag(m.Tags).Get("json"), "omitempty")
  71. return hasOptionalCommentTag || hasOptionalJsonTag
  72. }
  73. func apiTypeFilterFunc(c *generator.Context, t *types.Type) bool {
  74. // There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
  75. if strings.HasPrefix(t.Name.Name, "codecSelfer") {
  76. return false
  77. }
  78. pkg := c.Universe.Package(t.Name.Package)
  79. if hasOpenAPITagValue(pkg.Comments, tagValueTrue) {
  80. return !hasOpenAPITagValue(t.CommentLines, tagValueFalse)
  81. }
  82. if hasOpenAPITagValue(t.CommentLines, tagValueTrue) {
  83. return true
  84. }
  85. return false
  86. }
  87. const (
  88. specPackagePath = "github.com/go-openapi/spec"
  89. openAPICommonPackagePath = "k8s.io/kube-openapi/pkg/common"
  90. )
  91. // openApiGen produces a file with auto-generated OpenAPI functions.
  92. type openAPIGen struct {
  93. generator.DefaultGen
  94. // TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
  95. targetPackage string
  96. imports namer.ImportTracker
  97. }
  98. func newOpenAPIGen(sanitizedName string, targetPackage string) generator.Generator {
  99. return &openAPIGen{
  100. DefaultGen: generator.DefaultGen{
  101. OptionalName: sanitizedName,
  102. },
  103. imports: generator.NewImportTracker(),
  104. targetPackage: targetPackage,
  105. }
  106. }
  107. const nameTmpl = "schema_$.type|private$"
  108. func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems {
  109. // Have the raw namer for this file track what it imports.
  110. return namer.NameSystems{
  111. "raw": namer.NewRawNamer(g.targetPackage, g.imports),
  112. "private": &namer.NameStrategy{
  113. Join: func(pre string, in []string, post string) string {
  114. return strings.Join(in, "_")
  115. },
  116. PrependPackageNames: 4, // enough to fully qualify from k8s.io/api/...
  117. },
  118. }
  119. }
  120. func (g *openAPIGen) isOtherPackage(pkg string) bool {
  121. if pkg == g.targetPackage {
  122. return false
  123. }
  124. if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
  125. return false
  126. }
  127. return true
  128. }
  129. func (g *openAPIGen) Imports(c *generator.Context) []string {
  130. importLines := []string{}
  131. for _, singleImport := range g.imports.ImportLines() {
  132. importLines = append(importLines, singleImport)
  133. }
  134. return importLines
  135. }
  136. func argsFromType(t *types.Type) generator.Args {
  137. return generator.Args{
  138. "type": t,
  139. "ReferenceCallback": types.Ref(openAPICommonPackagePath, "ReferenceCallback"),
  140. "OpenAPIDefinition": types.Ref(openAPICommonPackagePath, "OpenAPIDefinition"),
  141. "SpecSchemaType": types.Ref(specPackagePath, "Schema"),
  142. }
  143. }
  144. func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
  145. sw := generator.NewSnippetWriter(w, c, "$", "$")
  146. sw.Do("func GetOpenAPIDefinitions(ref $.ReferenceCallback|raw$) map[string]$.OpenAPIDefinition|raw$ {\n", argsFromType(nil))
  147. sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
  148. for _, t := range c.Order {
  149. err := newOpenAPITypeWriter(sw, c).generateCall(t)
  150. if err != nil {
  151. return err
  152. }
  153. }
  154. sw.Do("}\n", nil)
  155. sw.Do("}\n\n", nil)
  156. return sw.Error()
  157. }
  158. func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
  159. klog.V(5).Infof("generating for type %v", t)
  160. sw := generator.NewSnippetWriter(w, c, "$", "$")
  161. err := newOpenAPITypeWriter(sw, c).generate(t)
  162. if err != nil {
  163. return err
  164. }
  165. return sw.Error()
  166. }
  167. func getJsonTags(m *types.Member) []string {
  168. jsonTag := reflect.StructTag(m.Tags).Get("json")
  169. if jsonTag == "" {
  170. return []string{}
  171. }
  172. return strings.Split(jsonTag, ",")
  173. }
  174. func getReferableName(m *types.Member) string {
  175. jsonTags := getJsonTags(m)
  176. if len(jsonTags) > 0 {
  177. if jsonTags[0] == "-" {
  178. return ""
  179. } else {
  180. return jsonTags[0]
  181. }
  182. } else {
  183. return m.Name
  184. }
  185. }
  186. func shouldInlineMembers(m *types.Member) bool {
  187. jsonTags := getJsonTags(m)
  188. return len(jsonTags) > 1 && jsonTags[1] == "inline"
  189. }
  190. type openAPITypeWriter struct {
  191. *generator.SnippetWriter
  192. context *generator.Context
  193. refTypes map[string]*types.Type
  194. GetDefinitionInterface *types.Type
  195. }
  196. func newOpenAPITypeWriter(sw *generator.SnippetWriter, c *generator.Context) openAPITypeWriter {
  197. return openAPITypeWriter{
  198. SnippetWriter: sw,
  199. context: c,
  200. refTypes: map[string]*types.Type{},
  201. }
  202. }
  203. func methodReturnsValue(mt *types.Type, pkg, name string) bool {
  204. if len(mt.Signature.Parameters) != 0 || len(mt.Signature.Results) != 1 {
  205. return false
  206. }
  207. r := mt.Signature.Results[0]
  208. return r.Name.Name == name && r.Name.Package == pkg
  209. }
  210. func hasOpenAPIDefinitionMethod(t *types.Type) bool {
  211. for mn, mt := range t.Methods {
  212. if mn != "OpenAPIDefinition" {
  213. continue
  214. }
  215. return methodReturnsValue(mt, openAPICommonPackagePath, "OpenAPIDefinition")
  216. }
  217. return false
  218. }
  219. func hasOpenAPIDefinitionMethods(t *types.Type) bool {
  220. var hasSchemaTypeMethod, hasOpenAPISchemaFormat bool
  221. for mn, mt := range t.Methods {
  222. switch mn {
  223. case "OpenAPISchemaType":
  224. hasSchemaTypeMethod = methodReturnsValue(mt, "", "[]string")
  225. case "OpenAPISchemaFormat":
  226. hasOpenAPISchemaFormat = methodReturnsValue(mt, "", "string")
  227. }
  228. }
  229. return hasSchemaTypeMethod && hasOpenAPISchemaFormat
  230. }
  231. // typeShortName returns short package name (e.g. the name x appears in package x definition) dot type name.
  232. func typeShortName(t *types.Type) string {
  233. return filepath.Base(t.Name.Package) + "." + t.Name.Name
  234. }
  235. func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]string, error) {
  236. var err error
  237. for _, m := range t.Members {
  238. if hasOpenAPITagValue(m.CommentLines, tagValueFalse) {
  239. continue
  240. }
  241. if shouldInlineMembers(&m) {
  242. required, err = g.generateMembers(m.Type, required)
  243. if err != nil {
  244. return required, err
  245. }
  246. continue
  247. }
  248. name := getReferableName(&m)
  249. if name == "" {
  250. continue
  251. }
  252. if !hasOptionalTag(&m) {
  253. required = append(required, name)
  254. }
  255. if err = g.generateProperty(&m, t); err != nil {
  256. klog.Errorf("Error when generating: %v, %v\n", name, m)
  257. return required, err
  258. }
  259. }
  260. return required, nil
  261. }
  262. func (g openAPITypeWriter) generateCall(t *types.Type) error {
  263. // Only generate for struct type and ignore the rest
  264. switch t.Kind {
  265. case types.Struct:
  266. args := argsFromType(t)
  267. g.Do("\"$.$\": ", t.Name)
  268. if hasOpenAPIDefinitionMethod(t) {
  269. g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args)
  270. } else {
  271. g.Do(nameTmpl+"(ref),\n", args)
  272. }
  273. }
  274. return g.Error()
  275. }
  276. func (g openAPITypeWriter) generate(t *types.Type) error {
  277. // Only generate for struct type and ignore the rest
  278. switch t.Kind {
  279. case types.Struct:
  280. if hasOpenAPIDefinitionMethod(t) {
  281. // already invoked directly
  282. return nil
  283. }
  284. args := argsFromType(t)
  285. g.Do("func "+nameTmpl+"(ref $.ReferenceCallback|raw$) $.OpenAPIDefinition|raw$ {\n", args)
  286. if hasOpenAPIDefinitionMethods(t) {
  287. g.Do("return $.OpenAPIDefinition|raw${\n"+
  288. "Schema: spec.Schema{\n"+
  289. "SchemaProps: spec.SchemaProps{\n", args)
  290. g.generateDescription(t.CommentLines)
  291. g.Do("Type:$.type|raw${}.OpenAPISchemaType(),\n"+
  292. "Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
  293. "},\n"+
  294. "},\n"+
  295. "}\n}\n\n", args)
  296. return nil
  297. }
  298. g.Do("return $.OpenAPIDefinition|raw${\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", args)
  299. g.generateDescription(t.CommentLines)
  300. g.Do("Type: []string{\"object\"},\n", nil)
  301. // write members into a temporary buffer, in order to postpone writing out the Properties field. We only do
  302. // that if it is not empty.
  303. propertiesBuf := bytes.Buffer{}
  304. bsw := g
  305. bsw.SnippetWriter = generator.NewSnippetWriter(&propertiesBuf, g.context, "$", "$")
  306. required, err := bsw.generateMembers(t, []string{})
  307. if err != nil {
  308. return err
  309. }
  310. if propertiesBuf.Len() > 0 {
  311. g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args)
  312. g.Do(strings.Replace(propertiesBuf.String(), "$", "$\"$\"$", -1), nil) // escape $ (used as delimiter of the templates)
  313. g.Do("},\n", nil)
  314. }
  315. if len(required) > 0 {
  316. g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
  317. }
  318. g.Do("},\n", nil)
  319. if err := g.generateStructExtensions(t); err != nil {
  320. return err
  321. }
  322. g.Do("},\n", nil)
  323. // Map order is undefined, sort them or we may get a different file generated each time.
  324. keys := []string{}
  325. for k := range g.refTypes {
  326. keys = append(keys, k)
  327. }
  328. sort.Strings(keys)
  329. deps := []string{}
  330. for _, k := range keys {
  331. v := g.refTypes[k]
  332. if t, _ := openapi.GetOpenAPITypeFormat(v.String()); t != "" {
  333. // This is a known type, we do not need a reference to it
  334. // Will eliminate special case of time.Time
  335. continue
  336. }
  337. deps = append(deps, k)
  338. }
  339. if len(deps) > 0 {
  340. g.Do("Dependencies: []string{\n", args)
  341. for _, k := range deps {
  342. g.Do("\"$.$\",", k)
  343. }
  344. g.Do("},\n", nil)
  345. }
  346. g.Do("}\n}\n\n", nil)
  347. }
  348. return nil
  349. }
  350. func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
  351. extensions, errors := parseExtensions(t.CommentLines)
  352. // Initially, we will only log struct extension errors.
  353. if len(errors) > 0 {
  354. for _, e := range errors {
  355. klog.V(2).Infof("[%s]: %s\n", t.String(), e)
  356. }
  357. }
  358. // TODO(seans3): Validate struct extensions here.
  359. g.emitExtensions(extensions)
  360. return nil
  361. }
  362. func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *types.Type) error {
  363. extensions, parseErrors := parseExtensions(m.CommentLines)
  364. validationErrors := validateMemberExtensions(extensions, m)
  365. errors := append(parseErrors, validationErrors...)
  366. // Initially, we will only log member extension errors.
  367. if len(errors) > 0 {
  368. errorPrefix := fmt.Sprintf("[%s] %s:", parent.String(), m.String())
  369. for _, e := range errors {
  370. klog.V(2).Infof("%s %s\n", errorPrefix, e)
  371. }
  372. }
  373. g.emitExtensions(extensions)
  374. return nil
  375. }
  376. func (g openAPITypeWriter) emitExtensions(extensions []extension) {
  377. // If any extensions exist, then emit code to create them.
  378. if len(extensions) == 0 {
  379. return
  380. }
  381. g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
  382. for _, extension := range extensions {
  383. g.Do("\"$.$\": ", extension.xName)
  384. if extension.hasMultipleValues() {
  385. g.Do("[]interface{}{\n", nil)
  386. }
  387. for _, value := range extension.values {
  388. g.Do("\"$.$\",\n", value)
  389. }
  390. if extension.hasMultipleValues() {
  391. g.Do("},\n", nil)
  392. }
  393. }
  394. g.Do("},\n},\n", nil)
  395. }
  396. // TODO(#44005): Move this validation outside of this generator (probably to policy verifier)
  397. func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type) error {
  398. // TODO: Remove patch struct tag validation because they we are now consuming OpenAPI on server.
  399. for _, tagKey := range tempPatchTags {
  400. structTagValue := reflect.StructTag(m.Tags).Get(tagKey)
  401. commentTagValue, err := getSingleTagsValue(m.CommentLines, tagKey)
  402. if err != nil {
  403. return err
  404. }
  405. if structTagValue != commentTagValue {
  406. return fmt.Errorf("Tags in comment and struct should match for member (%s) of (%s)",
  407. m.Name, parent.Name.String())
  408. }
  409. }
  410. return nil
  411. }
  412. func (g openAPITypeWriter) generateDescription(CommentLines []string) {
  413. var buffer bytes.Buffer
  414. delPrevChar := func() {
  415. if buffer.Len() > 0 {
  416. buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
  417. }
  418. }
  419. for _, line := range CommentLines {
  420. // Ignore all lines after ---
  421. if line == "---" {
  422. break
  423. }
  424. line = strings.TrimRight(line, " ")
  425. leading := strings.TrimLeft(line, " ")
  426. switch {
  427. case len(line) == 0: // Keep paragraphs
  428. delPrevChar()
  429. buffer.WriteString("\n\n")
  430. case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
  431. case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
  432. default:
  433. if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
  434. delPrevChar()
  435. line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
  436. } else {
  437. line += " "
  438. }
  439. buffer.WriteString(line)
  440. }
  441. }
  442. postDoc := strings.TrimRight(buffer.String(), "\n")
  443. postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
  444. postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
  445. postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
  446. postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
  447. postDoc = strings.Trim(postDoc, " ")
  448. if postDoc != "" {
  449. g.Do("Description: \"$.$\",\n", postDoc)
  450. }
  451. }
  452. func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type) error {
  453. name := getReferableName(m)
  454. if name == "" {
  455. return nil
  456. }
  457. if err := g.validatePatchTags(m, parent); err != nil {
  458. return err
  459. }
  460. g.Do("\"$.$\": {\n", name)
  461. if err := g.generateMemberExtensions(m, parent); err != nil {
  462. return err
  463. }
  464. g.Do("SchemaProps: spec.SchemaProps{\n", nil)
  465. g.generateDescription(m.CommentLines)
  466. jsonTags := getJsonTags(m)
  467. if len(jsonTags) > 1 && jsonTags[1] == "string" {
  468. g.generateSimpleProperty("string", "")
  469. g.Do("},\n},\n", nil)
  470. return nil
  471. }
  472. t := resolveAliasAndPtrType(m.Type)
  473. // If we can get a openAPI type and format for this type, we consider it to be simple property
  474. typeString, format := openapi.GetOpenAPITypeFormat(t.String())
  475. if typeString != "" {
  476. g.generateSimpleProperty(typeString, format)
  477. g.Do("},\n},\n", nil)
  478. return nil
  479. }
  480. switch t.Kind {
  481. case types.Builtin:
  482. return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", t)
  483. case types.Map:
  484. if err := g.generateMapProperty(t); err != nil {
  485. return err
  486. }
  487. case types.Slice, types.Array:
  488. if err := g.generateSliceProperty(t); err != nil {
  489. return err
  490. }
  491. case types.Struct, types.Interface:
  492. g.generateReferenceProperty(t)
  493. default:
  494. return fmt.Errorf("cannot generate spec for type %v", t)
  495. }
  496. g.Do("},\n},\n", nil)
  497. return g.Error()
  498. }
  499. func (g openAPITypeWriter) generateSimpleProperty(typeString, format string) {
  500. g.Do("Type: []string{\"$.$\"},\n", typeString)
  501. g.Do("Format: \"$.$\",\n", format)
  502. }
  503. func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
  504. g.refTypes[t.Name.String()] = t
  505. g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())
  506. }
  507. func resolveAliasAndPtrType(t *types.Type) *types.Type {
  508. var prev *types.Type
  509. for prev != t {
  510. prev = t
  511. if t.Kind == types.Alias {
  512. t = t.Underlying
  513. }
  514. if t.Kind == types.Pointer {
  515. t = t.Elem
  516. }
  517. }
  518. return t
  519. }
  520. func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
  521. keyType := resolveAliasAndPtrType(t.Key)
  522. elemType := resolveAliasAndPtrType(t.Elem)
  523. // According to OpenAPI examples, only map from string is supported
  524. if keyType.Name.Name != "string" {
  525. return fmt.Errorf("map with non-string keys are not supported by OpenAPI in %v", t)
  526. }
  527. g.Do("Type: []string{\"object\"},\n", nil)
  528. g.Do("AdditionalProperties: &spec.SchemaOrBool{\nAllows: true,\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
  529. typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
  530. if typeString != "" {
  531. g.generateSimpleProperty(typeString, format)
  532. g.Do("},\n},\n},\n", nil)
  533. return nil
  534. }
  535. switch elemType.Kind {
  536. case types.Builtin:
  537. return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
  538. case types.Struct:
  539. g.generateReferenceProperty(elemType)
  540. case types.Slice, types.Array:
  541. g.generateSliceProperty(elemType)
  542. default:
  543. return fmt.Errorf("map Element kind %v is not supported in %v", elemType.Kind, t.Name)
  544. }
  545. g.Do("},\n},\n},\n", nil)
  546. return nil
  547. }
  548. func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
  549. elemType := resolveAliasAndPtrType(t.Elem)
  550. g.Do("Type: []string{\"array\"},\n", nil)
  551. g.Do("Items: &spec.SchemaOrArray{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
  552. typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
  553. if typeString != "" {
  554. g.generateSimpleProperty(typeString, format)
  555. g.Do("},\n},\n},\n", nil)
  556. return nil
  557. }
  558. switch elemType.Kind {
  559. case types.Builtin:
  560. return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
  561. case types.Struct:
  562. g.generateReferenceProperty(elemType)
  563. case types.Slice, types.Array:
  564. g.generateSliceProperty(elemType)
  565. default:
  566. return fmt.Errorf("slice Element kind %v is not supported in %v", elemType.Kind, t)
  567. }
  568. g.Do("},\n},\n},\n", nil)
  569. return nil
  570. }