You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

model.go 62KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961
  1. // Copyright 2015 go-swagger maintainers
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package generator
  15. import (
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "log"
  20. "os"
  21. "path"
  22. "path/filepath"
  23. "sort"
  24. "strconv"
  25. "strings"
  26. "github.com/go-openapi/analysis"
  27. "github.com/go-openapi/loads"
  28. "github.com/go-openapi/spec"
  29. "github.com/go-openapi/swag"
  30. )
  31. const asMethod = "()"
  32. /*
  33. Rewrite specification document first:
  34. * anonymous objects
  35. * tuples
  36. * extensible objects (properties + additionalProperties)
  37. * AllOfs when they match the rewrite criteria (not a nullable allOf)
  38. Find string enums and generate specialized idiomatic enum with them
  39. Every action that happens tracks the path which is a linked list of refs
  40. */
  41. // GenerateDefinition generates a model file for a schema definition.
  42. func GenerateDefinition(modelNames []string, opts *GenOpts) error {
  43. if opts == nil {
  44. return errors.New("gen opts are required")
  45. }
  46. if opts.TemplateDir != "" {
  47. if err := templates.LoadDir(opts.TemplateDir); err != nil {
  48. return err
  49. }
  50. }
  51. if err := opts.CheckOpts(); err != nil {
  52. return err
  53. }
  54. // Load the spec
  55. specPath, specDoc, err := loadSpec(opts.Spec)
  56. if err != nil {
  57. return err
  58. }
  59. if len(modelNames) == 0 {
  60. for k := range specDoc.Spec().Definitions {
  61. modelNames = append(modelNames, k)
  62. }
  63. }
  64. for _, modelName := range modelNames {
  65. // lookup schema
  66. model, ok := specDoc.Spec().Definitions[modelName]
  67. if !ok {
  68. return fmt.Errorf("model %q not found in definitions given by %q", modelName, specPath)
  69. }
  70. // generate files
  71. generator := definitionGenerator{
  72. Name: modelName,
  73. Model: model,
  74. SpecDoc: specDoc,
  75. Target: filepath.Join(
  76. opts.Target,
  77. filepath.FromSlash(opts.LanguageOpts.ManglePackagePath(opts.ModelPackage, ""))),
  78. opts: opts,
  79. }
  80. if err := generator.Generate(); err != nil {
  81. return err
  82. }
  83. }
  84. return nil
  85. }
  86. type definitionGenerator struct {
  87. Name string
  88. Model spec.Schema
  89. SpecDoc *loads.Document
  90. Target string
  91. opts *GenOpts
  92. }
  93. func (m *definitionGenerator) Generate() error {
  94. mod, err := makeGenDefinition(m.Name, m.Target, m.Model, m.SpecDoc, m.opts)
  95. if err != nil {
  96. return fmt.Errorf("could not generate definitions for model %s on target %s: %v", m.Name, m.Target, err)
  97. }
  98. if m.opts.DumpData {
  99. bb, _ := json.MarshalIndent(swag.ToDynamicJSON(mod), "", " ")
  100. fmt.Fprintln(os.Stdout, string(bb))
  101. return nil
  102. }
  103. if m.opts.IncludeModel {
  104. log.Println("including additional model")
  105. if err := m.generateModel(mod); err != nil {
  106. return fmt.Errorf("could not generate model: %v", err)
  107. }
  108. }
  109. log.Println("generated model", m.Name)
  110. return nil
  111. }
  112. func (m *definitionGenerator) generateModel(g *GenDefinition) error {
  113. debugLog("rendering definitions for %+v", *g)
  114. return m.opts.renderDefinition(g)
  115. }
  116. func makeGenDefinition(name, pkg string, schema spec.Schema, specDoc *loads.Document, opts *GenOpts) (*GenDefinition, error) {
  117. gd, err := makeGenDefinitionHierarchy(name, pkg, "", schema, specDoc, opts)
  118. if err == nil && gd != nil {
  119. // before yielding the schema to the renderer, we check if the top-level Validate method gets some content
  120. // this means that the immediate content of the top level definitions has at least one validation.
  121. //
  122. // If none is found at this level and that no special case where no Validate() method is exposed at all
  123. // (e.g. io.ReadCloser and interface{} types and their aliases), then there is an empty Validate() method which
  124. // just return nil (the object abides by the runtime.Validatable interface, but knows it has nothing to validate).
  125. //
  126. // We do this at the top level because of the possibility of aliased types which always bubble up validation to types which
  127. // are referring to them. This results in correct but inelegant code with empty validations.
  128. gd.GenSchema.HasValidations = shallowValidationLookup(gd.GenSchema)
  129. }
  130. return gd, err
  131. }
  132. func shallowValidationLookup(sch GenSchema) bool {
  133. // scan top level need for validations
  134. //
  135. // NOTE: this supersedes the previous NeedsValidation flag
  136. // With the introduction of this shallow lookup, it is no more necessary
  137. // to establish a distinction between HasValidations (e.g. carries on validations)
  138. // and NeedsValidation (e.g. should have a Validate method with something in it).
  139. // The latter was almost not used anyhow.
  140. if sch.IsArray && sch.HasValidations {
  141. return true
  142. }
  143. if sch.IsStream || sch.IsInterface { // these types have no validation - aliased types on those do not implement the Validatable interface
  144. return false
  145. }
  146. if sch.Required || sch.IsCustomFormatter && !sch.IsStream {
  147. return true
  148. }
  149. if sch.MaxLength != nil || sch.MinLength != nil || sch.Pattern != "" || sch.MultipleOf != nil || sch.Minimum != nil || sch.Maximum != nil || len(sch.Enum) > 0 || len(sch.ItemsEnum) > 0 {
  150. return true
  151. }
  152. for _, a := range sch.AllOf {
  153. if a.HasValidations {
  154. return true
  155. }
  156. }
  157. for _, p := range sch.Properties {
  158. // Using a base type within another structure triggers validation of the base type.
  159. // The discriminator property in the base type definition itself does not.
  160. if (p.HasValidations || p.Required) && !(sch.IsBaseType && p.Name == sch.DiscriminatorField) || (p.IsAliased || p.IsComplexObject) && !(p.IsInterface || p.IsStream) {
  161. return true
  162. }
  163. }
  164. if sch.IsTuple && (sch.AdditionalItems != nil && (sch.AdditionalItems.HasValidations || sch.AdditionalItems.Required)) {
  165. return true
  166. }
  167. if sch.HasAdditionalProperties && (sch.AdditionalProperties.IsInterface || sch.AdditionalProperties.IsStream) {
  168. return false
  169. }
  170. if sch.HasAdditionalProperties && (sch.AdditionalProperties.HasValidations || sch.AdditionalProperties.Required || sch.AdditionalProperties.IsAliased && !(sch.AdditionalProperties.IsInterface || sch.AdditionalProperties.IsStream)) {
  171. return true
  172. }
  173. if sch.IsAliased && (sch.IsPrimitive && sch.HasValidations) { // non primitive aliased have either other attributes with validation (above) or shall not validate
  174. return true
  175. }
  176. if sch.HasBaseType || sch.IsSubType {
  177. return true
  178. }
  179. return false
  180. }
  181. func makeGenDefinitionHierarchy(name, pkg, container string, schema spec.Schema, specDoc *loads.Document, opts *GenOpts) (*GenDefinition, error) {
  182. // Check if model is imported from external package using x-go-type
  183. _, external := schema.Extensions[xGoType]
  184. receiver := "m"
  185. // models are resolved in the current package
  186. resolver := newTypeResolver("", specDoc)
  187. resolver.ModelName = name
  188. analyzed := analysis.New(specDoc.Spec())
  189. di := discriminatorInfo(analyzed)
  190. pg := schemaGenContext{
  191. Path: "",
  192. Name: name,
  193. Receiver: receiver,
  194. IndexVar: "i",
  195. ValueExpr: receiver,
  196. Schema: schema,
  197. Required: false,
  198. TypeResolver: resolver,
  199. Named: true,
  200. ExtraSchemas: make(map[string]GenSchema),
  201. Discrimination: di,
  202. Container: container,
  203. IncludeValidator: opts.IncludeValidator,
  204. IncludeModel: opts.IncludeModel,
  205. StrictAdditionalProperties: opts.StrictAdditionalProperties,
  206. }
  207. if err := pg.makeGenSchema(); err != nil {
  208. return nil, fmt.Errorf("could not generate schema for %s: %v", name, err)
  209. }
  210. dsi, ok := di.Discriminators["#/definitions/"+name]
  211. if ok {
  212. // when these 2 are true then the schema will render as an interface
  213. pg.GenSchema.IsBaseType = true
  214. pg.GenSchema.IsExported = true
  215. pg.GenSchema.DiscriminatorField = dsi.FieldName
  216. if pg.GenSchema.Discriminates == nil {
  217. pg.GenSchema.Discriminates = make(map[string]string)
  218. }
  219. pg.GenSchema.Discriminates[name] = dsi.GoType
  220. pg.GenSchema.DiscriminatorValue = name
  221. for _, v := range dsi.Children {
  222. pg.GenSchema.Discriminates[v.FieldValue] = v.GoType
  223. }
  224. for j := range pg.GenSchema.Properties {
  225. if !strings.HasSuffix(pg.GenSchema.Properties[j].ValueExpression, asMethod) {
  226. pg.GenSchema.Properties[j].ValueExpression += asMethod
  227. }
  228. }
  229. }
  230. dse, ok := di.Discriminated["#/definitions/"+name]
  231. if ok {
  232. pg.GenSchema.DiscriminatorField = dse.FieldName
  233. pg.GenSchema.DiscriminatorValue = dse.FieldValue
  234. pg.GenSchema.IsSubType = true
  235. knownProperties := make(map[string]struct{})
  236. // find the referenced definitions
  237. // check if it has a discriminator defined
  238. // when it has a discriminator get the schema and run makeGenSchema for it.
  239. // replace the ref with this new genschema
  240. swsp := specDoc.Spec()
  241. for i, ss := range schema.AllOf {
  242. ref := ss.Ref
  243. for ref.String() != "" {
  244. var rsch *spec.Schema
  245. var err error
  246. rsch, err = spec.ResolveRef(swsp, &ref)
  247. if err != nil {
  248. return nil, err
  249. }
  250. ref = rsch.Ref
  251. if rsch != nil && rsch.Ref.String() != "" {
  252. ref = rsch.Ref
  253. continue
  254. }
  255. ref = spec.Ref{}
  256. if rsch != nil && rsch.Discriminator != "" {
  257. gs, err := makeGenDefinitionHierarchy(strings.TrimPrefix(ss.Ref.String(), "#/definitions/"), pkg, pg.GenSchema.Name, *rsch, specDoc, opts)
  258. if err != nil {
  259. return nil, err
  260. }
  261. gs.GenSchema.IsBaseType = true
  262. gs.GenSchema.IsExported = true
  263. pg.GenSchema.AllOf[i] = gs.GenSchema
  264. schPtr := &(pg.GenSchema.AllOf[i])
  265. if schPtr.AdditionalItems != nil {
  266. schPtr.AdditionalItems.IsBaseType = true
  267. }
  268. if schPtr.AdditionalProperties != nil {
  269. schPtr.AdditionalProperties.IsBaseType = true
  270. }
  271. for j := range schPtr.Properties {
  272. schPtr.Properties[j].IsBaseType = true
  273. knownProperties[schPtr.Properties[j].Name] = struct{}{}
  274. }
  275. }
  276. }
  277. }
  278. // dedupe the fields
  279. alreadySeen := make(map[string]struct{})
  280. for i, ss := range pg.GenSchema.AllOf {
  281. var remainingProperties GenSchemaList
  282. for _, p := range ss.Properties {
  283. if _, ok := knownProperties[p.Name]; !ok || ss.IsBaseType {
  284. if _, seen := alreadySeen[p.Name]; !seen {
  285. remainingProperties = append(remainingProperties, p)
  286. alreadySeen[p.Name] = struct{}{}
  287. }
  288. }
  289. }
  290. pg.GenSchema.AllOf[i].Properties = remainingProperties
  291. }
  292. }
  293. defaultImports := []string{
  294. "github.com/go-openapi/errors",
  295. "github.com/go-openapi/runtime",
  296. "github.com/go-openapi/swag",
  297. "github.com/go-openapi/validate",
  298. }
  299. return &GenDefinition{
  300. GenCommon: GenCommon{
  301. Copyright: opts.Copyright,
  302. TargetImportPath: filepath.ToSlash(opts.LanguageOpts.baseImport(opts.Target)),
  303. },
  304. Package: opts.LanguageOpts.ManglePackageName(path.Base(filepath.ToSlash(pkg)), "definitions"),
  305. GenSchema: pg.GenSchema,
  306. DependsOn: pg.Dependencies,
  307. DefaultImports: defaultImports,
  308. ExtraSchemas: gatherExtraSchemas(pg.ExtraSchemas),
  309. Imports: findImports(&pg.GenSchema),
  310. External: external,
  311. }, nil
  312. }
  313. func findImports(sch *GenSchema) map[string]string {
  314. imp := map[string]string{}
  315. t := sch.resolvedType
  316. if t.Pkg != "" && t.PkgAlias != "" {
  317. imp[t.PkgAlias] = t.Pkg
  318. }
  319. if sch.Items != nil {
  320. sub := findImports(sch.Items)
  321. for k, v := range sub {
  322. imp[k] = v
  323. }
  324. }
  325. if sch.AdditionalItems != nil {
  326. sub := findImports(sch.AdditionalItems)
  327. for k, v := range sub {
  328. imp[k] = v
  329. }
  330. }
  331. if sch.Object != nil {
  332. sub := findImports(sch.Object)
  333. for k, v := range sub {
  334. imp[k] = v
  335. }
  336. }
  337. if sch.Properties != nil {
  338. for _, p := range sch.Properties {
  339. sub := findImports(&p)
  340. for k, v := range sub {
  341. imp[k] = v
  342. }
  343. }
  344. }
  345. if sch.AdditionalProperties != nil {
  346. sub := findImports(sch.AdditionalProperties)
  347. for k, v := range sub {
  348. imp[k] = v
  349. }
  350. }
  351. if sch.AllOf != nil {
  352. for _, p := range sch.AllOf {
  353. sub := findImports(&p)
  354. for k, v := range sub {
  355. imp[k] = v
  356. }
  357. }
  358. }
  359. return imp
  360. }
  361. type schemaGenContext struct {
  362. Required bool
  363. AdditionalProperty bool
  364. Untyped bool
  365. Named bool
  366. RefHandled bool
  367. IsVirtual bool
  368. IsTuple bool
  369. IncludeValidator bool
  370. IncludeModel bool
  371. StrictAdditionalProperties bool
  372. Index int
  373. Path string
  374. Name string
  375. ParamName string
  376. Accessor string
  377. Receiver string
  378. IndexVar string
  379. KeyVar string
  380. ValueExpr string
  381. Container string
  382. Schema spec.Schema
  383. TypeResolver *typeResolver
  384. GenSchema GenSchema
  385. Dependencies []string // NOTE: Dependencies is actually set nowhere
  386. ExtraSchemas map[string]GenSchema
  387. Discriminator *discor
  388. Discriminated *discee
  389. Discrimination *discInfo
  390. }
  391. func (sg *schemaGenContext) NewSliceBranch(schema *spec.Schema) *schemaGenContext {
  392. debugLog("new slice branch %s (model: %s)", sg.Name, sg.TypeResolver.ModelName)
  393. pg := sg.shallowClone()
  394. indexVar := pg.IndexVar
  395. if pg.Path == "" {
  396. pg.Path = "strconv.Itoa(" + indexVar + ")"
  397. } else {
  398. pg.Path = pg.Path + "+ \".\" + strconv.Itoa(" + indexVar + ")"
  399. }
  400. // check who is parent, if it's a base type then rewrite the value expression
  401. if sg.Discrimination != nil && sg.Discrimination.Discriminators != nil {
  402. _, rewriteValueExpr := sg.Discrimination.Discriminators["#/definitions/"+sg.TypeResolver.ModelName]
  403. if (pg.IndexVar == "i" && rewriteValueExpr) || sg.GenSchema.ElemType.IsBaseType {
  404. if !sg.GenSchema.IsAliased {
  405. pg.ValueExpr = sg.Receiver + "." + swag.ToJSONName(sg.GenSchema.Name) + "Field"
  406. } else {
  407. pg.ValueExpr = sg.Receiver
  408. }
  409. }
  410. }
  411. sg.GenSchema.IsBaseType = sg.GenSchema.ElemType.HasDiscriminator
  412. pg.IndexVar = indexVar + "i"
  413. pg.ValueExpr = pg.ValueExpr + "[" + indexVar + "]"
  414. pg.Schema = *schema
  415. pg.Required = false
  416. if sg.IsVirtual {
  417. pg.TypeResolver = sg.TypeResolver.NewWithModelName(sg.TypeResolver.ModelName)
  418. }
  419. // when this is an anonymous complex object, this needs to become a ref
  420. return pg
  421. }
  422. func (sg *schemaGenContext) NewAdditionalItems(schema *spec.Schema) *schemaGenContext {
  423. debugLog("new additional items\n")
  424. pg := sg.shallowClone()
  425. indexVar := pg.IndexVar
  426. pg.Name = sg.Name + " items"
  427. itemsLen := 0
  428. if sg.Schema.Items != nil {
  429. itemsLen = sg.Schema.Items.Len()
  430. }
  431. var mod string
  432. if itemsLen > 0 {
  433. mod = "+" + strconv.Itoa(itemsLen)
  434. }
  435. if pg.Path == "" {
  436. pg.Path = "strconv.Itoa(" + indexVar + mod + ")"
  437. } else {
  438. pg.Path = pg.Path + "+ \".\" + strconv.Itoa(" + indexVar + mod + ")"
  439. }
  440. pg.IndexVar = indexVar
  441. pg.ValueExpr = sg.ValueExpr + "." + pascalize(sg.GoName()) + "Items[" + indexVar + "]"
  442. pg.Schema = spec.Schema{}
  443. if schema != nil {
  444. pg.Schema = *schema
  445. }
  446. pg.Required = false
  447. return pg
  448. }
  449. func (sg *schemaGenContext) NewTupleElement(schema *spec.Schema, index int) *schemaGenContext {
  450. debugLog("New tuple element\n")
  451. pg := sg.shallowClone()
  452. if pg.Path == "" {
  453. pg.Path = "\"" + strconv.Itoa(index) + "\""
  454. } else {
  455. pg.Path = pg.Path + "+ \".\"+\"" + strconv.Itoa(index) + "\""
  456. }
  457. pg.ValueExpr = pg.ValueExpr + ".P" + strconv.Itoa(index)
  458. pg.Required = true
  459. pg.IsTuple = true
  460. pg.Schema = *schema
  461. return pg
  462. }
  463. func (sg *schemaGenContext) NewStructBranch(name string, schema spec.Schema) *schemaGenContext {
  464. debugLog("new struct branch %s (parent %s)", sg.Name, sg.Container)
  465. pg := sg.shallowClone()
  466. if sg.Path == "" {
  467. pg.Path = fmt.Sprintf("%q", name)
  468. } else {
  469. pg.Path = pg.Path + "+\".\"+" + fmt.Sprintf("%q", name)
  470. }
  471. pg.Name = name
  472. pg.ValueExpr = pg.ValueExpr + "." + pascalize(goName(&schema, name))
  473. pg.Schema = schema
  474. for _, fn := range sg.Schema.Required {
  475. if name == fn {
  476. pg.Required = true
  477. break
  478. }
  479. }
  480. debugLog("made new struct branch %s (parent %s)", pg.Name, pg.Container)
  481. return pg
  482. }
  483. func (sg *schemaGenContext) shallowClone() *schemaGenContext {
  484. debugLog("cloning context %s\n", sg.Name)
  485. pg := new(schemaGenContext)
  486. *pg = *sg
  487. if pg.Container == "" {
  488. pg.Container = sg.Name
  489. }
  490. pg.GenSchema = GenSchema{}
  491. pg.Dependencies = nil
  492. pg.Named = false
  493. pg.Index = 0
  494. pg.IsTuple = false
  495. pg.IncludeValidator = sg.IncludeValidator
  496. pg.IncludeModel = sg.IncludeModel
  497. pg.StrictAdditionalProperties = sg.StrictAdditionalProperties
  498. return pg
  499. }
  500. func (sg *schemaGenContext) NewCompositionBranch(schema spec.Schema, index int) *schemaGenContext {
  501. debugLog("new composition branch %s (parent: %s, index: %d)", sg.Name, sg.Container, index)
  502. pg := sg.shallowClone()
  503. pg.Schema = schema
  504. pg.Name = "AO" + strconv.Itoa(index)
  505. if sg.Name != sg.TypeResolver.ModelName {
  506. pg.Name = sg.Name + pg.Name
  507. }
  508. pg.Index = index
  509. debugLog("made new composition branch %s (parent: %s)", pg.Name, pg.Container)
  510. return pg
  511. }
  512. func (sg *schemaGenContext) NewAdditionalProperty(schema spec.Schema) *schemaGenContext {
  513. debugLog("new additional property %s (expr: %s)", sg.Name, sg.ValueExpr)
  514. pg := sg.shallowClone()
  515. pg.Schema = schema
  516. if pg.KeyVar == "" {
  517. pg.ValueExpr = sg.ValueExpr
  518. }
  519. pg.KeyVar += "k"
  520. pg.ValueExpr += "[" + pg.KeyVar + "]"
  521. pg.Path = pg.KeyVar
  522. pg.GenSchema.Suffix = "Value"
  523. if sg.Path != "" {
  524. pg.Path = sg.Path + "+\".\"+" + pg.KeyVar
  525. }
  526. // propagates the special IsNullable override for maps of slices and
  527. // maps of aliased types.
  528. pg.GenSchema.IsMapNullOverride = sg.GenSchema.IsMapNullOverride
  529. return pg
  530. }
  531. func hasSliceValidations(model *spec.Schema) (hasSliceValidations bool) {
  532. hasSliceValidations = model.MaxItems != nil || model.MinItems != nil || model.UniqueItems || len(model.Enum) > 0
  533. return
  534. }
  535. func hasValidations(model *spec.Schema, isRequired bool) (hasValidation bool) {
  536. // NOTE: needsValidation has gone deprecated and is replaced by top-level's shallowValidationLookup()
  537. hasNumberValidation := model.Maximum != nil || model.Minimum != nil || model.MultipleOf != nil
  538. hasStringValidation := model.MaxLength != nil || model.MinLength != nil || model.Pattern != ""
  539. hasEnum := len(model.Enum) > 0
  540. // since this was added to deal with discriminator, we'll fix this when testing discriminated types
  541. simpleObject := len(model.Properties) > 0 && model.Discriminator == ""
  542. // lift validations from allOf branches
  543. hasAllOfValidation := false
  544. for _, s := range model.AllOf {
  545. hasAllOfValidation = hasValidations(&s, false)
  546. hasAllOfValidation = s.Ref.String() != "" || hasAllOfValidation
  547. if hasAllOfValidation {
  548. break
  549. }
  550. }
  551. hasValidation = hasNumberValidation || hasStringValidation || hasSliceValidations(model) || hasEnum || simpleObject || hasAllOfValidation || isRequired
  552. return
  553. }
  554. // handleFormatConflicts handles all conflicting model properties when a format is set
  555. func handleFormatConflicts(model *spec.Schema) {
  556. switch model.Format {
  557. case "date", "datetime", "uuid", "bsonobjectid", "base64", "duration":
  558. model.MinLength = nil
  559. model.MaxLength = nil
  560. model.Pattern = ""
  561. // more cases should be inserted here if they arise
  562. }
  563. }
  564. func (sg *schemaGenContext) schemaValidations() sharedValidations {
  565. model := sg.Schema
  566. // resolve any conflicting properties if the model has a format
  567. handleFormatConflicts(&model)
  568. isRequired := sg.Required
  569. if model.Default != nil || model.ReadOnly {
  570. // when readOnly or default is specified, this disables Required validation (Swagger-specific)
  571. isRequired = false
  572. }
  573. hasSliceValidations := model.MaxItems != nil || model.MinItems != nil || model.UniqueItems || len(model.Enum) > 0
  574. hasValidations := hasValidations(&model, isRequired)
  575. s := sharedValidationsFromSchema(model, sg.Required)
  576. s.HasValidations = hasValidations
  577. s.HasSliceValidations = hasSliceValidations
  578. return s
  579. }
  580. func mergeValidation(other *schemaGenContext) bool {
  581. // NOTE: NeesRequired and NeedsValidation are deprecated
  582. if other.GenSchema.AdditionalProperties != nil && other.GenSchema.AdditionalProperties.HasValidations {
  583. return true
  584. }
  585. if other.GenSchema.AdditionalItems != nil && other.GenSchema.AdditionalItems.HasValidations {
  586. return true
  587. }
  588. for _, sch := range other.GenSchema.AllOf {
  589. if sch.HasValidations {
  590. return true
  591. }
  592. }
  593. return other.GenSchema.HasValidations
  594. }
  595. func (sg *schemaGenContext) MergeResult(other *schemaGenContext, liftsRequired bool) {
  596. sg.GenSchema.HasValidations = sg.GenSchema.HasValidations || mergeValidation(other)
  597. if liftsRequired && other.GenSchema.AdditionalProperties != nil && other.GenSchema.AdditionalProperties.Required {
  598. sg.GenSchema.Required = true
  599. }
  600. if liftsRequired && other.GenSchema.Required {
  601. sg.GenSchema.Required = other.GenSchema.Required
  602. }
  603. if other.GenSchema.HasBaseType {
  604. sg.GenSchema.HasBaseType = other.GenSchema.HasBaseType
  605. }
  606. sg.Dependencies = append(sg.Dependencies, other.Dependencies...)
  607. // lift extra schemas
  608. for k, v := range other.ExtraSchemas {
  609. sg.ExtraSchemas[k] = v
  610. }
  611. if other.GenSchema.IsMapNullOverride {
  612. sg.GenSchema.IsMapNullOverride = true
  613. }
  614. }
  615. func (sg *schemaGenContext) buildProperties() error {
  616. debugLog("building properties %s (parent: %s)", sg.Name, sg.Container)
  617. for k, v := range sg.Schema.Properties {
  618. debugLogAsJSON("building property %s[%q] (tup: %t) (BaseType: %t)",
  619. sg.Name, k, sg.IsTuple, sg.GenSchema.IsBaseType, sg.Schema)
  620. debugLog("property %s[%q] (tup: %t) HasValidations: %t)",
  621. sg.Name, k, sg.IsTuple, sg.GenSchema.HasValidations)
  622. // check if this requires de-anonymizing, if so lift this as a new struct and extra schema
  623. tpe, err := sg.TypeResolver.ResolveSchema(&v, true, sg.IsTuple || containsString(sg.Schema.Required, k))
  624. if sg.Schema.Discriminator == k {
  625. tpe.IsNullable = false
  626. }
  627. if err != nil {
  628. return err
  629. }
  630. vv := v
  631. var hasValidation bool
  632. if tpe.IsComplexObject && tpe.IsAnonymous && len(v.Properties) > 0 {
  633. // this is an anonymous complex construct: build a new new type for it
  634. pg := sg.makeNewStruct(sg.Name+swag.ToGoName(k), v)
  635. pg.IsTuple = sg.IsTuple
  636. if sg.Path != "" {
  637. pg.Path = sg.Path + "+ \".\"+" + fmt.Sprintf("%q", k)
  638. } else {
  639. pg.Path = fmt.Sprintf("%q", k)
  640. }
  641. if err := pg.makeGenSchema(); err != nil {
  642. return err
  643. }
  644. if v.Discriminator != "" {
  645. pg.GenSchema.IsBaseType = true
  646. pg.GenSchema.IsExported = true
  647. pg.GenSchema.HasBaseType = true
  648. }
  649. vv = *spec.RefProperty("#/definitions/" + pg.Name)
  650. hasValidation = pg.GenSchema.HasValidations
  651. sg.ExtraSchemas[pg.Name] = pg.GenSchema
  652. // NOTE: MergeResult lifts validation status and extra schemas
  653. sg.MergeResult(pg, false)
  654. }
  655. emprop := sg.NewStructBranch(k, vv)
  656. emprop.IsTuple = sg.IsTuple
  657. if err := emprop.makeGenSchema(); err != nil {
  658. return err
  659. }
  660. // whatever the validations says, if we have an interface{}, do not validate
  661. // NOTE: this may be the case when the type is left empty and we get a Enum validation.
  662. if emprop.GenSchema.IsInterface || emprop.GenSchema.IsStream {
  663. emprop.GenSchema.HasValidations = false
  664. } else if hasValidation || emprop.GenSchema.HasValidations || emprop.GenSchema.Required || emprop.GenSchema.IsAliased || len(emprop.GenSchema.AllOf) > 0 {
  665. emprop.GenSchema.HasValidations = true
  666. sg.GenSchema.HasValidations = true
  667. }
  668. // generates format validation on property
  669. emprop.GenSchema.HasValidations = emprop.GenSchema.HasValidations || (tpe.IsCustomFormatter && !tpe.IsStream) || (tpe.IsArray && tpe.ElemType.IsCustomFormatter && !tpe.ElemType.IsStream)
  670. if emprop.Schema.Ref.String() != "" {
  671. // expand the schema of this property, so we take informed decisions about its type
  672. ref := emprop.Schema.Ref
  673. var sch *spec.Schema
  674. for ref.String() != "" {
  675. var rsch *spec.Schema
  676. var err error
  677. specDoc := sg.TypeResolver.Doc
  678. rsch, err = spec.ResolveRef(specDoc.Spec(), &ref)
  679. if err != nil {
  680. return err
  681. }
  682. ref = rsch.Ref
  683. if rsch != nil && rsch.Ref.String() != "" {
  684. ref = rsch.Ref
  685. continue
  686. }
  687. ref = spec.Ref{}
  688. sch = rsch
  689. }
  690. if emprop.Discrimination != nil {
  691. if _, ok := emprop.Discrimination.Discriminators[emprop.Schema.Ref.String()]; ok {
  692. emprop.GenSchema.IsBaseType = true
  693. emprop.GenSchema.IsNullable = false
  694. emprop.GenSchema.HasBaseType = true
  695. }
  696. if _, ok := emprop.Discrimination.Discriminated[emprop.Schema.Ref.String()]; ok {
  697. emprop.GenSchema.IsSubType = true
  698. }
  699. }
  700. // set property name
  701. var nm = filepath.Base(emprop.Schema.Ref.GetURL().Fragment)
  702. tr := sg.TypeResolver.NewWithModelName(goName(&emprop.Schema, swag.ToGoName(nm)))
  703. ttpe, err := tr.ResolveSchema(sch, false, true)
  704. if err != nil {
  705. return err
  706. }
  707. if ttpe.IsAliased {
  708. emprop.GenSchema.IsAliased = true
  709. }
  710. // lift validations
  711. hv := hasValidations(sch, false)
  712. // include format validation, excluding binary
  713. hv = hv || (ttpe.IsCustomFormatter && !ttpe.IsStream) || (ttpe.IsArray && ttpe.ElemType.IsCustomFormatter && !ttpe.ElemType.IsStream)
  714. // a base type property is always validated against the base type
  715. // exception: for the base type definition itself (see shallowValidationLookup())
  716. if (hv || emprop.GenSchema.IsBaseType) && !(emprop.GenSchema.IsInterface || emprop.GenSchema.IsStream) {
  717. emprop.GenSchema.HasValidations = true
  718. }
  719. if ttpe.HasAdditionalItems && sch.AdditionalItems.Schema != nil {
  720. // when AdditionalItems specifies a Schema, there is a validation
  721. // check if we stepped upon an exception
  722. child, err := tr.ResolveSchema(sch.AdditionalItems.Schema, false, true)
  723. if err != nil {
  724. return err
  725. }
  726. if !child.IsInterface && !child.IsStream {
  727. emprop.GenSchema.HasValidations = true
  728. }
  729. }
  730. if ttpe.IsMap && sch.AdditionalProperties != nil && sch.AdditionalProperties.Schema != nil {
  731. // when AdditionalProperties specifies a Schema, there is a validation
  732. // check if we stepped upon an exception
  733. child, err := tr.ResolveSchema(sch.AdditionalProperties.Schema, false, true)
  734. if err != nil {
  735. return err
  736. }
  737. if !child.IsInterface && !child.IsStream {
  738. emprop.GenSchema.HasValidations = true
  739. }
  740. }
  741. }
  742. if sg.Schema.Discriminator == k {
  743. // this is the discriminator property:
  744. // it is required, but forced as non-nullable,
  745. // since we never fill it with a zero-value
  746. // TODO: when no other property than discriminator, there is no validation
  747. emprop.GenSchema.IsNullable = false
  748. }
  749. if emprop.GenSchema.IsBaseType {
  750. sg.GenSchema.HasBaseType = true
  751. }
  752. sg.MergeResult(emprop, false)
  753. // when discriminated, data is accessed via a getter func
  754. if emprop.GenSchema.HasDiscriminator {
  755. emprop.GenSchema.ValueExpression += asMethod
  756. }
  757. emprop.GenSchema.Extensions = emprop.Schema.Extensions
  758. // set custom serializer tag
  759. if customTag, found := emprop.Schema.Extensions[xGoCustomTag]; found {
  760. emprop.GenSchema.CustomTag = customTag.(string)
  761. }
  762. sg.GenSchema.Properties = append(sg.GenSchema.Properties, emprop.GenSchema)
  763. }
  764. sort.Sort(sg.GenSchema.Properties)
  765. return nil
  766. }
  767. func (sg *schemaGenContext) buildAllOf() error {
  768. if len(sg.Schema.AllOf) == 0 {
  769. return nil
  770. }
  771. var hasArray, hasNonArray int
  772. sort.Sort(sg.GenSchema.AllOf)
  773. if sg.Container == "" {
  774. sg.Container = sg.Name
  775. }
  776. debugLogAsJSON("building all of for %d entries", len(sg.Schema.AllOf), sg.Schema)
  777. for i, sch := range sg.Schema.AllOf {
  778. tpe, ert := sg.TypeResolver.ResolveSchema(&sch, sch.Ref.String() == "", false)
  779. if ert != nil {
  780. return ert
  781. }
  782. // check for multiple arrays in allOf branches.
  783. // Although a valid JSON-Schema construct, it is not suited for serialization.
  784. // This is the same if we attempt to serialize an array with another object.
  785. // We issue a generation warning on this.
  786. if tpe.IsArray {
  787. hasArray++
  788. } else {
  789. hasNonArray++
  790. }
  791. debugLogAsJSON("trying", sch)
  792. if (tpe.IsAnonymous && len(sch.AllOf) > 0) || (sch.Ref.String() == "" && !tpe.IsComplexObject && (tpe.IsArray || tpe.IsInterface || tpe.IsPrimitive)) {
  793. // cases where anonymous structures cause the creation of a new type:
  794. // - nested allOf: this one is itself a AllOf: build a new type for it
  795. // - anonymous simple types for edge cases: array, primitive, interface{}
  796. // NOTE: when branches are aliased or anonymous, the nullable property in the branch type is lost.
  797. name := swag.ToVarName(goName(&sch, sg.Name+"AllOf"+strconv.Itoa(i)))
  798. debugLog("building anonymous nested allOf in %s: %s", sg.Name, name)
  799. ng := sg.makeNewStruct(name, sch)
  800. if err := ng.makeGenSchema(); err != nil {
  801. return err
  802. }
  803. newsch := spec.RefProperty("#/definitions/" + ng.Name)
  804. sg.Schema.AllOf[i] = *newsch
  805. pg := sg.NewCompositionBranch(*newsch, i)
  806. if err := pg.makeGenSchema(); err != nil {
  807. return err
  808. }
  809. // lift extra schemas & validations from new type
  810. pg.MergeResult(ng, true)
  811. // lift validations when complex or ref'ed:
  812. // - parent always calls its Validatable child
  813. // - child may or may not have validations
  814. //
  815. // Exception: child is not Validatable when interface or stream
  816. if !pg.GenSchema.IsInterface && !pg.GenSchema.IsStream {
  817. sg.GenSchema.HasValidations = true
  818. }
  819. // add the newly created type to the list of schemas to be rendered inline
  820. pg.ExtraSchemas[ng.Name] = ng.GenSchema
  821. sg.MergeResult(pg, true)
  822. sg.GenSchema.AllOf = append(sg.GenSchema.AllOf, pg.GenSchema)
  823. continue
  824. }
  825. comprop := sg.NewCompositionBranch(sch, i)
  826. if err := comprop.makeGenSchema(); err != nil {
  827. return err
  828. }
  829. if comprop.GenSchema.IsMap && comprop.GenSchema.HasAdditionalProperties && comprop.GenSchema.AdditionalProperties != nil && !comprop.GenSchema.IsInterface {
  830. // the anonymous branch is a map for AdditionalProperties: rewrite value expression
  831. comprop.GenSchema.ValueExpression = comprop.GenSchema.ValueExpression + "." + comprop.Name
  832. comprop.GenSchema.AdditionalProperties.ValueExpression = comprop.GenSchema.ValueExpression + "[" + comprop.GenSchema.AdditionalProperties.KeyVar + "]"
  833. }
  834. // lift validations when complex or ref'ed
  835. if (comprop.GenSchema.IsComplexObject || comprop.Schema.Ref.String() != "") && !(comprop.GenSchema.IsInterface || comprop.GenSchema.IsStream) {
  836. comprop.GenSchema.HasValidations = true
  837. }
  838. sg.MergeResult(comprop, true)
  839. sg.GenSchema.AllOf = append(sg.GenSchema.AllOf, comprop.GenSchema)
  840. }
  841. if hasArray > 1 || (hasArray > 0 && hasNonArray > 0) {
  842. log.Printf("warning: cannot generate serializable allOf with conflicting array definitions in %s", sg.Container)
  843. }
  844. sg.GenSchema.IsNullable = true
  845. // prevent IsAliased to bubble up (e.g. when a single branch is itself aliased)
  846. sg.GenSchema.IsAliased = sg.GenSchema.IsAliased && len(sg.GenSchema.AllOf) < 2
  847. return nil
  848. }
  849. type mapStack struct {
  850. Type *spec.Schema
  851. Next *mapStack
  852. Previous *mapStack
  853. ValueRef *schemaGenContext
  854. Context *schemaGenContext
  855. NewObj *schemaGenContext
  856. }
  857. func newMapStack(context *schemaGenContext) (first, last *mapStack, err error) {
  858. ms := &mapStack{
  859. Type: &context.Schema,
  860. Context: context,
  861. }
  862. l := ms
  863. for l.HasMore() {
  864. tpe, err := l.Context.TypeResolver.ResolveSchema(l.Type.AdditionalProperties.Schema, true, true)
  865. if err != nil {
  866. return nil, nil, err
  867. }
  868. if !tpe.IsMap {
  869. //reached the end of the rabbit hole
  870. if tpe.IsComplexObject && tpe.IsAnonymous {
  871. // found an anonymous object: create the struct from a newly created definition
  872. nw := l.Context.makeNewStruct(l.Context.Name+" Anon", *l.Type.AdditionalProperties.Schema)
  873. sch := spec.RefProperty("#/definitions/" + nw.Name)
  874. l.NewObj = nw
  875. l.Type.AdditionalProperties.Schema = sch
  876. l.ValueRef = l.Context.NewAdditionalProperty(*sch)
  877. }
  878. // other cases where to stop are: a $ref or a simple object
  879. break
  880. }
  881. // continue digging for maps
  882. l.Next = &mapStack{
  883. Previous: l,
  884. Type: l.Type.AdditionalProperties.Schema,
  885. Context: l.Context.NewAdditionalProperty(*l.Type.AdditionalProperties.Schema),
  886. }
  887. l = l.Next
  888. }
  889. //return top and bottom entries of this stack of AdditionalProperties
  890. return ms, l, nil
  891. }
  892. // Build rewinds the stack of additional properties, building schemas from bottom to top
  893. func (mt *mapStack) Build() error {
  894. if mt.NewObj == nil && mt.ValueRef == nil && mt.Next == nil && mt.Previous == nil {
  895. csch := mt.Type.AdditionalProperties.Schema
  896. cp := mt.Context.NewAdditionalProperty(*csch)
  897. d := mt.Context.TypeResolver.Doc
  898. asch, err := analysis.Schema(analysis.SchemaOpts{
  899. Root: d.Spec(),
  900. BasePath: d.SpecFilePath(),
  901. Schema: csch,
  902. })
  903. if err != nil {
  904. return err
  905. }
  906. cp.Required = !asch.IsSimpleSchema && !asch.IsMap
  907. // when the schema is an array or an alias, this may result in inconsistent
  908. // nullable status between the map element and the array element (resp. the aliased type).
  909. //
  910. // Example: when an object has no property and only additionalProperties,
  911. // which turn out to be arrays of some other object.
  912. // save the initial override
  913. hadOverride := cp.GenSchema.IsMapNullOverride
  914. if err := cp.makeGenSchema(); err != nil {
  915. return err
  916. }
  917. // if we have an override at the top of stack, propagates it down nested arrays
  918. if hadOverride && cp.GenSchema.IsArray {
  919. // do it for nested arrays: override is also about map[string][][]... constructs
  920. it := &cp.GenSchema
  921. for it.Items != nil && it.IsArray {
  922. it.Items.IsMapNullOverride = hadOverride
  923. it = it.Items
  924. }
  925. }
  926. // cover other cases than arrays (aliased types)
  927. cp.GenSchema.IsMapNullOverride = hadOverride
  928. mt.Context.MergeResult(cp, false)
  929. mt.Context.GenSchema.AdditionalProperties = &cp.GenSchema
  930. // lift validations
  931. if (csch.Ref.String() != "" || cp.GenSchema.IsAliased) && !(cp.GenSchema.IsInterface || cp.GenSchema.IsStream) {
  932. // - we stopped on a ref, or anything else that require we call its Validate() method
  933. // - if the alias / ref is on an interface (or stream) type: no validation
  934. mt.Context.GenSchema.HasValidations = true
  935. mt.Context.GenSchema.AdditionalProperties.HasValidations = true
  936. }
  937. debugLog("early mapstack exit, nullable: %t for %s", cp.GenSchema.IsNullable, cp.GenSchema.Name)
  938. return nil
  939. }
  940. cur := mt
  941. for cur != nil {
  942. if cur.NewObj != nil {
  943. // a new model has been created during the stack construction (new ref on anonymous object)
  944. if err := cur.NewObj.makeGenSchema(); err != nil {
  945. return err
  946. }
  947. }
  948. if cur.ValueRef != nil {
  949. if err := cur.ValueRef.makeGenSchema(); err != nil {
  950. return nil
  951. }
  952. }
  953. if cur.NewObj != nil {
  954. // newly created model from anonymous object is declared as extra schema
  955. cur.Context.MergeResult(cur.NewObj, false)
  956. // propagates extra schemas
  957. cur.Context.ExtraSchemas[cur.NewObj.Name] = cur.NewObj.GenSchema
  958. }
  959. if cur.ValueRef != nil {
  960. // this is the genSchema for this new anonymous AdditionalProperty
  961. if err := cur.Context.makeGenSchema(); err != nil {
  962. return err
  963. }
  964. // if there is a ValueRef, we must have a NewObj (from newMapStack() construction)
  965. cur.ValueRef.GenSchema.HasValidations = cur.NewObj.GenSchema.HasValidations
  966. cur.Context.MergeResult(cur.ValueRef, false)
  967. cur.Context.GenSchema.AdditionalProperties = &cur.ValueRef.GenSchema
  968. }
  969. if cur.Previous != nil {
  970. // we have a parent schema: build a schema for current AdditionalProperties
  971. if err := cur.Context.makeGenSchema(); err != nil {
  972. return err
  973. }
  974. }
  975. if cur.Next != nil {
  976. // we previously made a child schema: lifts things from that one
  977. // - Required is not lifted (in a cascade of maps, only the last element is actually checked for Required)
  978. cur.Context.MergeResult(cur.Next.Context, false)
  979. cur.Context.GenSchema.AdditionalProperties = &cur.Next.Context.GenSchema
  980. // lift validations
  981. c := &cur.Next.Context.GenSchema
  982. if (cur.Next.Context.Schema.Ref.String() != "" || c.IsAliased) && !(c.IsInterface || c.IsStream) {
  983. // - we stopped on a ref, or anything else that require we call its Validate()
  984. // - if the alias / ref is on an interface (or stream) type: no validation
  985. cur.Context.GenSchema.HasValidations = true
  986. cur.Context.GenSchema.AdditionalProperties.HasValidations = true
  987. }
  988. }
  989. if cur.ValueRef != nil {
  990. cur.Context.MergeResult(cur.ValueRef, false)
  991. cur.Context.GenSchema.AdditionalProperties = &cur.ValueRef.GenSchema
  992. }
  993. if cur.Context.GenSchema.AdditionalProperties != nil {
  994. // propagate overrides up the resolved schemas, but leaves any ExtraSchema untouched
  995. cur.Context.GenSchema.AdditionalProperties.IsMapNullOverride = cur.Context.GenSchema.IsMapNullOverride
  996. }
  997. cur = cur.Previous
  998. }
  999. return nil
  1000. }
  1001. func (mt *mapStack) HasMore() bool {
  1002. return mt.Type.AdditionalProperties != nil && (mt.Type.AdditionalProperties.Schema != nil || mt.Type.AdditionalProperties.Allows)
  1003. }
  1004. /* currently unused:
  1005. func (mt *mapStack) Dict() map[string]interface{} {
  1006. res := make(map[string]interface{})
  1007. res["context"] = mt.Context.Schema
  1008. if mt.Next != nil {
  1009. res["next"] = mt.Next.Dict()
  1010. }
  1011. if mt.NewObj != nil {
  1012. res["obj"] = mt.NewObj.Schema
  1013. }
  1014. if mt.ValueRef != nil {
  1015. res["value"] = mt.ValueRef.Schema
  1016. }
  1017. return res
  1018. }
  1019. */
  1020. func (sg *schemaGenContext) buildAdditionalProperties() error {
  1021. if sg.Schema.AdditionalProperties == nil {
  1022. return nil
  1023. }
  1024. addp := *sg.Schema.AdditionalProperties
  1025. wantsAdditional := addp.Schema != nil || addp.Allows
  1026. sg.GenSchema.HasAdditionalProperties = wantsAdditional
  1027. if !wantsAdditional {
  1028. return nil
  1029. }
  1030. // flag swap
  1031. if sg.GenSchema.IsComplexObject {
  1032. sg.GenSchema.IsAdditionalProperties = true
  1033. sg.GenSchema.IsComplexObject = false
  1034. sg.GenSchema.IsMap = false
  1035. }
  1036. if addp.Schema == nil {
  1037. // this is for AdditionalProperties:true|false
  1038. if addp.Allows {
  1039. // additionalProperties: true is rendered as: map[string]interface{}
  1040. addp.Schema = &spec.Schema{}
  1041. addp.Schema.Typed("object", "")
  1042. sg.GenSchema.HasAdditionalProperties = true
  1043. sg.GenSchema.IsComplexObject = false
  1044. sg.GenSchema.IsMap = true
  1045. sg.GenSchema.ValueExpression += "." + swag.ToGoName(sg.Name+" additionalProperties")
  1046. cp := sg.NewAdditionalProperty(*addp.Schema)
  1047. cp.Name += "AdditionalProperties"
  1048. cp.Required = false
  1049. if err := cp.makeGenSchema(); err != nil {
  1050. return err
  1051. }
  1052. sg.MergeResult(cp, false)
  1053. sg.GenSchema.AdditionalProperties = &cp.GenSchema
  1054. debugLog("added interface{} schema for additionalProperties[allows == true], IsInterface=%t", cp.GenSchema.IsInterface)
  1055. }
  1056. return nil
  1057. }
  1058. if !sg.GenSchema.IsMap && (sg.GenSchema.IsAdditionalProperties && sg.Named) {
  1059. // we have a complex object with an AdditionalProperties schema
  1060. tpe, ert := sg.TypeResolver.ResolveSchema(addp.Schema, addp.Schema.Ref.String() == "", false)
  1061. if ert != nil {
  1062. return ert
  1063. }
  1064. if tpe.IsComplexObject && tpe.IsAnonymous {
  1065. // if the AdditionalProperties is an anonymous complex object, generate a new type for it
  1066. pg := sg.makeNewStruct(sg.Name+" Anon", *addp.Schema)
  1067. if err := pg.makeGenSchema(); err != nil {
  1068. return err
  1069. }
  1070. sg.MergeResult(pg, false)
  1071. sg.ExtraSchemas[pg.Name] = pg.GenSchema
  1072. sg.Schema.AdditionalProperties.Schema = spec.RefProperty("#/definitions/" + pg.Name)
  1073. sg.IsVirtual = true
  1074. comprop := sg.NewAdditionalProperty(*sg.Schema.AdditionalProperties.Schema)
  1075. if err := comprop.makeGenSchema(); err != nil {
  1076. return err
  1077. }
  1078. comprop.GenSchema.Required = true
  1079. comprop.GenSchema.HasValidations = true
  1080. comprop.GenSchema.ValueExpression = sg.GenSchema.ValueExpression + "." + swag.ToGoName(sg.GenSchema.Name) + "[" + comprop.KeyVar + "]"
  1081. sg.GenSchema.AdditionalProperties = &comprop.GenSchema
  1082. sg.GenSchema.HasAdditionalProperties = true
  1083. sg.GenSchema.ValueExpression += "." + swag.ToGoName(sg.GenSchema.Name)
  1084. sg.MergeResult(comprop, false)
  1085. return nil
  1086. }
  1087. // this is a regular named schema for AdditionalProperties
  1088. sg.GenSchema.ValueExpression += "." + swag.ToGoName(sg.GenSchema.Name)
  1089. comprop := sg.NewAdditionalProperty(*addp.Schema)
  1090. d := sg.TypeResolver.Doc
  1091. asch, err := analysis.Schema(analysis.SchemaOpts{
  1092. Root: d.Spec(),
  1093. BasePath: d.SpecFilePath(),
  1094. Schema: addp.Schema,
  1095. })
  1096. if err != nil {
  1097. return err
  1098. }
  1099. comprop.Required = !asch.IsSimpleSchema && !asch.IsMap
  1100. if err := comprop.makeGenSchema(); err != nil {
  1101. return err
  1102. }
  1103. sg.MergeResult(comprop, false)
  1104. sg.GenSchema.AdditionalProperties = &comprop.GenSchema
  1105. sg.GenSchema.AdditionalProperties.ValueExpression = sg.GenSchema.ValueExpression + "[" + comprop.KeyVar + "]"
  1106. // rewrite value expression for arrays and arrays of arrays in maps (rendered as map[string][][]...)
  1107. if sg.GenSchema.AdditionalProperties.IsArray {
  1108. // maps of slices are where an override may take effect
  1109. sg.GenSchema.AdditionalProperties.Items.IsMapNullOverride = sg.GenSchema.AdditionalProperties.IsMapNullOverride
  1110. sg.GenSchema.AdditionalProperties.Items.ValueExpression = sg.GenSchema.ValueExpression + "[" + comprop.KeyVar + "]" + "[" + sg.GenSchema.AdditionalProperties.IndexVar + "]"
  1111. ap := sg.GenSchema.AdditionalProperties.Items
  1112. for ap != nil && ap.IsArray {
  1113. ap.Items.IsMapNullOverride = ap.IsMapNullOverride
  1114. ap.Items.ValueExpression = ap.ValueExpression + "[" + ap.IndexVar + "]"
  1115. ap = ap.Items
  1116. }
  1117. }
  1118. // lift validation
  1119. if (sg.GenSchema.AdditionalProperties.IsComplexObject || sg.GenSchema.AdditionalProperties.IsAliased || sg.GenSchema.AdditionalProperties.Required) && !(sg.GenSchema.AdditionalProperties.IsInterface || sg.GenSchema.IsStream) {
  1120. sg.GenSchema.HasValidations = true
  1121. }
  1122. return nil
  1123. }
  1124. if sg.GenSchema.IsMap && wantsAdditional {
  1125. // this is itself an AdditionalProperties schema with some AdditionalProperties.
  1126. // this also runs for aliased map types (with zero properties save additionalProperties)
  1127. //
  1128. // find out how deep this rabbit hole goes
  1129. // descend, unwind and rewrite
  1130. // This needs to be depth first, so it first goes as deep as it can and then
  1131. // builds the result in reverse order.
  1132. _, ls, err := newMapStack(sg)
  1133. if err != nil {
  1134. return err
  1135. }
  1136. return ls.Build()
  1137. }
  1138. if sg.GenSchema.IsAdditionalProperties && !sg.Named {
  1139. // for an anonymous object, first build the new object
  1140. // and then replace the current one with a $ref to the
  1141. // new object
  1142. newObj := sg.makeNewStruct(sg.GenSchema.Name+" P"+strconv.Itoa(sg.Index), sg.Schema)
  1143. if err := newObj.makeGenSchema(); err != nil {
  1144. return err
  1145. }
  1146. hasMapNullOverride := sg.GenSchema.IsMapNullOverride
  1147. sg.GenSchema = GenSchema{}
  1148. sg.Schema = *spec.RefProperty("#/definitions/" + newObj.Name)
  1149. if err := sg.makeGenSchema(); err != nil {
  1150. return err
  1151. }
  1152. sg.MergeResult(newObj, false)
  1153. sg.GenSchema.IsMapNullOverride = hasMapNullOverride
  1154. if sg.GenSchema.IsArray {
  1155. sg.GenSchema.Items.IsMapNullOverride = hasMapNullOverride
  1156. }
  1157. sg.GenSchema.HasValidations = newObj.GenSchema.HasValidations
  1158. sg.ExtraSchemas[newObj.Name] = newObj.GenSchema
  1159. return nil
  1160. }
  1161. return nil
  1162. }
  1163. func (sg *schemaGenContext) makeNewStruct(name string, schema spec.Schema) *schemaGenContext {
  1164. debugLog("making new struct: name: %s, container: %s", name, sg.Container)
  1165. sp := sg.TypeResolver.Doc.Spec()
  1166. name = swag.ToGoName(name)
  1167. if sg.TypeResolver.ModelName != sg.Name {
  1168. name = swag.ToGoName(sg.TypeResolver.ModelName + " " + name)
  1169. }
  1170. if sp.Definitions == nil {
  1171. sp.Definitions = make(spec.Definitions)
  1172. }
  1173. sp.Definitions[name] = schema
  1174. pg := schemaGenContext{
  1175. Path: "",
  1176. Name: name,
  1177. Receiver: sg.Receiver,
  1178. IndexVar: "i",
  1179. ValueExpr: sg.Receiver,
  1180. Schema: schema,
  1181. Required: false,
  1182. Named: true,
  1183. ExtraSchemas: make(map[string]GenSchema),
  1184. Discrimination: sg.Discrimination,
  1185. Container: sg.Container,
  1186. IncludeValidator: sg.IncludeValidator,
  1187. IncludeModel: sg.IncludeModel,
  1188. StrictAdditionalProperties: sg.StrictAdditionalProperties,
  1189. }
  1190. if schema.Ref.String() == "" {
  1191. pg.TypeResolver = sg.TypeResolver.NewWithModelName(name)
  1192. }
  1193. pg.GenSchema.IsVirtual = true
  1194. sg.ExtraSchemas[name] = pg.GenSchema
  1195. return &pg
  1196. }
  1197. func (sg *schemaGenContext) buildArray() error {
  1198. tpe, err := sg.TypeResolver.ResolveSchema(sg.Schema.Items.Schema, true, false)
  1199. if err != nil {
  1200. return err
  1201. }
  1202. // check if the element is a complex object, if so generate a new type for it
  1203. if tpe.IsComplexObject && tpe.IsAnonymous {
  1204. pg := sg.makeNewStruct(sg.Name+" items"+strconv.Itoa(sg.Index), *sg.Schema.Items.Schema)
  1205. if err := pg.makeGenSchema(); err != nil {
  1206. return err
  1207. }
  1208. sg.MergeResult(pg, false)
  1209. sg.ExtraSchemas[pg.Name] = pg.GenSchema
  1210. sg.Schema.Items.Schema = spec.RefProperty("#/definitions/" + pg.Name)
  1211. sg.IsVirtual = true
  1212. return sg.makeGenSchema()
  1213. }
  1214. // create the generation schema for items
  1215. elProp := sg.NewSliceBranch(sg.Schema.Items.Schema)
  1216. // when building a slice of maps, the map item is not required
  1217. // items from maps of aliased or nullable type remain required
  1218. // NOTE(fredbi): since this is reset below, this Required = true serves the obscure purpose
  1219. // of indirectly lifting validations from the slice. This is carried on differently now.
  1220. // elProp.Required = true
  1221. if err := elProp.makeGenSchema(); err != nil {
  1222. return err
  1223. }
  1224. sg.MergeResult(elProp, false)
  1225. sg.GenSchema.IsBaseType = elProp.GenSchema.IsBaseType
  1226. sg.GenSchema.ItemsEnum = elProp.GenSchema.Enum
  1227. elProp.GenSchema.Suffix = "Items"
  1228. elProp.GenSchema.IsNullable = tpe.IsNullable && !tpe.HasDiscriminator
  1229. if elProp.GenSchema.IsNullable {
  1230. sg.GenSchema.GoType = "[]*" + elProp.GenSchema.GoType
  1231. } else {
  1232. sg.GenSchema.GoType = "[]" + elProp.GenSchema.GoType
  1233. }
  1234. sg.GenSchema.IsArray = true
  1235. schemaCopy := elProp.GenSchema
  1236. schemaCopy.Required = false
  1237. // validations of items
  1238. hv := hasValidations(sg.Schema.Items.Schema, false)
  1239. // include format validation, excluding binary
  1240. hv = hv || (schemaCopy.IsCustomFormatter && !schemaCopy.IsStream) || (schemaCopy.IsArray && schemaCopy.ElemType.IsCustomFormatter && !schemaCopy.ElemType.IsStream)
  1241. // base types of polymorphic types must be validated
  1242. // NOTE: IsNullable is not useful to figure out a validation: we use Refed and IsAliased below instead
  1243. if hv || elProp.GenSchema.IsBaseType {
  1244. schemaCopy.HasValidations = true
  1245. }
  1246. if (elProp.Schema.Ref.String() != "" || elProp.GenSchema.IsAliased) && !(elProp.GenSchema.IsInterface || elProp.GenSchema.IsStream) {
  1247. schemaCopy.HasValidations = true
  1248. }
  1249. // lift validations
  1250. sg.GenSchema.HasValidations = sg.GenSchema.HasValidations || schemaCopy.HasValidations
  1251. sg.GenSchema.HasSliceValidations = hasSliceValidations(&sg.Schema)
  1252. // prevents bubbling custom formatter flag
  1253. sg.GenSchema.IsCustomFormatter = false
  1254. sg.GenSchema.Items = &schemaCopy
  1255. if sg.Named {
  1256. sg.GenSchema.AliasedType = sg.GenSchema.GoType
  1257. }
  1258. return nil
  1259. }
  1260. func (sg *schemaGenContext) buildItems() error {
  1261. if sg.Schema.Items == nil {
  1262. // in swagger, arrays MUST have an items schema
  1263. return nil
  1264. }
  1265. // in Items spec, we have either Schema (array) or Schemas (tuple)
  1266. presentsAsSingle := sg.Schema.Items.Schema != nil
  1267. if presentsAsSingle && sg.Schema.AdditionalItems != nil { // unsure if this a valid of invalid schema
  1268. return fmt.Errorf("single schema (%s) can't have additional items", sg.Name)
  1269. }
  1270. if presentsAsSingle {
  1271. return sg.buildArray()
  1272. }
  1273. // This is a tuple, build a new model that represents this
  1274. if sg.Named {
  1275. sg.GenSchema.Name = sg.Name
  1276. sg.GenSchema.GoType = sg.TypeResolver.goTypeName(sg.Name)
  1277. for i, s := range sg.Schema.Items.Schemas {
  1278. elProp := sg.NewTupleElement(&s, i)
  1279. if s.Ref.String() == "" {
  1280. tpe, err := sg.TypeResolver.ResolveSchema(&s, s.Ref.String() == "", true)
  1281. if err != nil {
  1282. return err
  1283. }
  1284. if tpe.IsComplexObject && tpe.IsAnonymous {
  1285. // if the tuple element is an anonymous complex object, build a new type for it
  1286. pg := sg.makeNewStruct(sg.Name+" Items"+strconv.Itoa(i), s)
  1287. if err := pg.makeGenSchema(); err != nil {
  1288. return err
  1289. }
  1290. elProp.Schema = *spec.RefProperty("#/definitions/" + pg.Name)
  1291. elProp.MergeResult(pg, false)
  1292. elProp.ExtraSchemas[pg.Name] = pg.GenSchema
  1293. }
  1294. }
  1295. if err := elProp.makeGenSchema(); err != nil {
  1296. return err
  1297. }
  1298. if elProp.GenSchema.IsInterface || elProp.GenSchema.IsStream {
  1299. elProp.GenSchema.HasValidations = false
  1300. }
  1301. sg.MergeResult(elProp, false)
  1302. elProp.GenSchema.Name = "p" + strconv.Itoa(i)
  1303. sg.GenSchema.Properties = append(sg.GenSchema.Properties, elProp.GenSchema)
  1304. sg.GenSchema.IsTuple = true
  1305. }
  1306. return nil
  1307. }
  1308. // for an anonymous object, first build the new object
  1309. // and then replace the current one with a $ref to the
  1310. // new tuple object
  1311. var sch spec.Schema
  1312. sch.Typed("object", "")
  1313. sch.Properties = make(map[string]spec.Schema, len(sg.Schema.Items.Schemas))
  1314. for i, v := range sg.Schema.Items.Schemas {
  1315. sch.Required = append(sch.Required, "P"+strconv.Itoa(i))
  1316. sch.Properties["P"+strconv.Itoa(i)] = v
  1317. }
  1318. sch.AdditionalItems = sg.Schema.AdditionalItems
  1319. tup := sg.makeNewStruct(sg.GenSchema.Name+"Tuple"+strconv.Itoa(sg.Index), sch)
  1320. tup.IsTuple = true
  1321. if err := tup.makeGenSchema(); err != nil {
  1322. return err
  1323. }
  1324. tup.GenSchema.IsTuple = true
  1325. tup.GenSchema.IsComplexObject = false
  1326. tup.GenSchema.Title = tup.GenSchema.Name + " a representation of an anonymous Tuple type"
  1327. tup.GenSchema.Description = ""
  1328. sg.ExtraSchemas[tup.Name] = tup.GenSchema
  1329. sg.Schema = *spec.RefProperty("#/definitions/" + tup.Name)
  1330. if err := sg.makeGenSchema(); err != nil {
  1331. return err
  1332. }
  1333. sg.MergeResult(tup, false)
  1334. return nil
  1335. }
  1336. func (sg *schemaGenContext) buildAdditionalItems() error {
  1337. wantsAdditionalItems :=
  1338. sg.Schema.AdditionalItems != nil &&
  1339. (sg.Schema.AdditionalItems.Allows || sg.Schema.AdditionalItems.Schema != nil)
  1340. sg.GenSchema.HasAdditionalItems = wantsAdditionalItems
  1341. if wantsAdditionalItems {
  1342. // check if the element is a complex object, if so generate a new type for it
  1343. tpe, err := sg.TypeResolver.ResolveSchema(sg.Schema.AdditionalItems.Schema, true, true)
  1344. if err != nil {
  1345. return err
  1346. }
  1347. if tpe.IsComplexObject && tpe.IsAnonymous {
  1348. pg := sg.makeNewStruct(sg.Name+" Items", *sg.Schema.AdditionalItems.Schema)
  1349. if err := pg.makeGenSchema(); err != nil {
  1350. return err
  1351. }
  1352. sg.Schema.AdditionalItems.Schema = spec.RefProperty("#/definitions/" + pg.Name)
  1353. pg.GenSchema.HasValidations = true
  1354. sg.MergeResult(pg, false)
  1355. sg.ExtraSchemas[pg.Name] = pg.GenSchema
  1356. }
  1357. it := sg.NewAdditionalItems(sg.Schema.AdditionalItems.Schema)
  1358. // if AdditionalItems are themselves arrays, bump the index var
  1359. if tpe.IsArray {
  1360. it.IndexVar += "i"
  1361. }
  1362. if tpe.IsInterface {
  1363. it.Untyped = true
  1364. }
  1365. if err := it.makeGenSchema(); err != nil {
  1366. return err
  1367. }
  1368. // lift validations when complex is not anonymous or ref'ed
  1369. if (tpe.IsComplexObject || it.Schema.Ref.String() != "") && !(tpe.IsInterface || tpe.IsStream) {
  1370. it.GenSchema.HasValidations = true
  1371. }
  1372. sg.MergeResult(it, true)
  1373. sg.GenSchema.AdditionalItems = &it.GenSchema
  1374. }
  1375. return nil
  1376. }
  1377. func (sg *schemaGenContext) buildXMLName() error {
  1378. if sg.Schema.XML == nil {
  1379. return nil
  1380. }
  1381. sg.GenSchema.XMLName = sg.Name
  1382. if sg.Schema.XML.Name != "" {
  1383. sg.GenSchema.XMLName = sg.Schema.XML.Name
  1384. if sg.Schema.XML.Attribute {
  1385. sg.GenSchema.XMLName += ",attr"
  1386. }
  1387. }
  1388. return nil
  1389. }
  1390. func (sg *schemaGenContext) shortCircuitNamedRef() (bool, error) {
  1391. // This if block ensures that a struct gets
  1392. // rendered with the ref as embedded ref.
  1393. //
  1394. // NOTE: this assumes that all $ref point to a definition,
  1395. // i.e. the spec is canonical, as guaranteed by minimal flattening.
  1396. //
  1397. // TODO: RefHandled is actually set nowhere
  1398. if sg.RefHandled || !sg.Named || sg.Schema.Ref.String() == "" {
  1399. return false, nil
  1400. }
  1401. debugLogAsJSON("short circuit named ref: %q", sg.Schema.Ref.String(), sg.Schema)
  1402. // Simple aliased types (arrays, maps and primitives)
  1403. //
  1404. // Before deciding to make a struct with a composition branch (below),
  1405. // check if the $ref points to a simple type or polymorphic (base) type.
  1406. //
  1407. // If this is the case, just realias this simple type, without creating a struct.
  1408. asch, era := analysis.Schema(analysis.SchemaOpts{
  1409. Root: sg.TypeResolver.Doc.Spec(),
  1410. BasePath: sg.TypeResolver.Doc.SpecFilePath(),
  1411. Schema: &sg.Schema,
  1412. })
  1413. if era != nil {
  1414. return false, era
  1415. }
  1416. if asch.IsArray || asch.IsMap || asch.IsKnownType || asch.IsBaseType {
  1417. tpx, ers := sg.TypeResolver.ResolveSchema(&sg.Schema, false, true)
  1418. if ers != nil {
  1419. return false, ers
  1420. }
  1421. tpe := resolvedType{}
  1422. tpe.IsMap = asch.IsMap
  1423. tpe.IsArray = asch.IsArray
  1424. tpe.IsPrimitive = asch.IsKnownType
  1425. tpe.IsAliased = true
  1426. tpe.AliasedType = ""
  1427. tpe.IsComplexObject = false
  1428. tpe.IsAnonymous = false
  1429. tpe.IsCustomFormatter = false
  1430. tpe.IsBaseType = tpx.IsBaseType
  1431. tpe.GoType = sg.TypeResolver.goTypeName(path.Base(sg.Schema.Ref.String()))
  1432. tpe.IsNullable = tpx.IsNullable // TODO
  1433. tpe.IsInterface = tpx.IsInterface
  1434. tpe.IsStream = tpx.IsStream
  1435. tpe.SwaggerType = tpx.SwaggerType
  1436. sch := spec.Schema{}
  1437. pg := sg.makeNewStruct(sg.Name, sch)
  1438. if err := pg.makeGenSchema(); err != nil {
  1439. return true, err
  1440. }
  1441. sg.MergeResult(pg, true)
  1442. sg.GenSchema = pg.GenSchema
  1443. sg.GenSchema.resolvedType = tpe
  1444. sg.GenSchema.resolvedType.IsSuperAlias = true
  1445. sg.GenSchema.IsBaseType = tpe.IsBaseType
  1446. return true, nil
  1447. }
  1448. // Aliased object: use golang struct composition.
  1449. // This is rendered as a struct with type field, i.e. :
  1450. // Alias struct {
  1451. // AliasedType
  1452. // }
  1453. nullableOverride := sg.GenSchema.IsNullable
  1454. tpe := resolvedType{}
  1455. tpe.GoType = sg.TypeResolver.goTypeName(sg.Name)
  1456. tpe.SwaggerType = "object"
  1457. tpe.IsComplexObject = true
  1458. tpe.IsMap = false
  1459. tpe.IsArray = false
  1460. tpe.IsAnonymous = false
  1461. tpe.IsNullable = sg.TypeResolver.IsNullable(&sg.Schema)
  1462. item := sg.NewCompositionBranch(sg.Schema, 0)
  1463. if err := item.makeGenSchema(); err != nil {
  1464. return true, err
  1465. }
  1466. sg.GenSchema.resolvedType = tpe
  1467. sg.GenSchema.IsNullable = sg.GenSchema.IsNullable || nullableOverride
  1468. // prevent format from bubbling up in composed type
  1469. item.GenSchema.IsCustomFormatter = false
  1470. sg.MergeResult(item, true)
  1471. sg.GenSchema.AllOf = append(sg.GenSchema.AllOf, item.GenSchema)
  1472. return true, nil
  1473. }
  1474. // liftSpecialAllOf attempts to simplify the rendering of allOf constructs by lifting simple things into the current schema.
  1475. func (sg *schemaGenContext) liftSpecialAllOf() error {
  1476. // if there is only a $ref or a primitive and an x-isnullable schema then this is a nullable pointer
  1477. // so this should not compose several objects, just 1
  1478. // if there is a ref with a discriminator then we look for x-class on the current definition to know
  1479. // the value of the discriminator to instantiate the class
  1480. if len(sg.Schema.AllOf) < 2 {
  1481. return nil
  1482. }
  1483. var seenSchema int
  1484. var seenNullable bool
  1485. var schemaToLift spec.Schema
  1486. for _, sch := range sg.Schema.AllOf {
  1487. tpe, err := sg.TypeResolver.ResolveSchema(&sch, true, true)
  1488. if err != nil {
  1489. return err
  1490. }
  1491. if sg.TypeResolver.IsNullable(&sch) {
  1492. seenNullable = true
  1493. }
  1494. if len(sch.Type) > 0 || len(sch.Properties) > 0 || sch.Ref.GetURL() != nil || len(sch.AllOf) > 0 {
  1495. seenSchema++
  1496. if seenSchema > 1 {
  1497. // won't do anything if several candidates for a lift
  1498. break
  1499. }
  1500. if (!tpe.IsAnonymous && tpe.IsComplexObject) || tpe.IsPrimitive {
  1501. // lifting complex objects here results in inlined structs in the model
  1502. schemaToLift = sch
  1503. }
  1504. }
  1505. }
  1506. if seenSchema == 1 {
  1507. // when there only a single schema to lift in allOf, replace the schema by its allOf definition
  1508. debugLog("lifted schema in allOf for %s", sg.Name)
  1509. sg.Schema = schemaToLift
  1510. sg.GenSchema.IsNullable = seenNullable
  1511. }
  1512. return nil
  1513. }
  1514. func (sg *schemaGenContext) buildAliased() error {
  1515. if !sg.GenSchema.IsPrimitive && !sg.GenSchema.IsMap && !sg.GenSchema.IsArray && !sg.GenSchema.IsInterface {
  1516. return nil
  1517. }
  1518. if sg.GenSchema.IsPrimitive {
  1519. if sg.GenSchema.SwaggerType == "string" && sg.GenSchema.SwaggerFormat == "" {
  1520. sg.GenSchema.IsAliased = sg.GenSchema.GoType != sg.GenSchema.SwaggerType
  1521. }
  1522. if sg.GenSchema.IsNullable && sg.Named {
  1523. sg.GenSchema.IsNullable = false
  1524. }
  1525. }
  1526. if sg.GenSchema.IsInterface {
  1527. sg.GenSchema.IsAliased = sg.GenSchema.GoType != iface
  1528. }
  1529. if sg.GenSchema.IsMap {
  1530. sg.GenSchema.IsAliased = !strings.HasPrefix(sg.GenSchema.GoType, "map[")
  1531. }
  1532. if sg.GenSchema.IsArray {
  1533. sg.GenSchema.IsAliased = !strings.HasPrefix(sg.GenSchema.GoType, "[]")
  1534. }
  1535. return nil
  1536. }
  1537. func (sg *schemaGenContext) GoName() string {
  1538. return goName(&sg.Schema, sg.Name)
  1539. }
  1540. func goName(sch *spec.Schema, orig string) string {
  1541. name, _ := sch.Extensions.GetString(xGoName)
  1542. if name != "" {
  1543. return name
  1544. }
  1545. return orig
  1546. }
  1547. func (sg *schemaGenContext) checkNeedsPointer(outer *GenSchema, sch *GenSchema, elem *GenSchema) {
  1548. derefType := strings.TrimPrefix(elem.GoType, "*")
  1549. switch {
  1550. case outer.IsAliased && !strings.HasSuffix(outer.AliasedType, "*"+derefType):
  1551. // override nullability of map of primitive elements: render element of aliased or anonymous map as a pointer
  1552. outer.AliasedType = strings.TrimSuffix(outer.AliasedType, derefType) + "*" + derefType
  1553. case sch != nil:
  1554. // nullable primitive
  1555. if sch.IsAnonymous && !strings.HasSuffix(outer.GoType, "*"+derefType) {
  1556. sch.GoType = strings.TrimSuffix(sch.GoType, derefType) + "*" + derefType
  1557. }
  1558. case outer.IsAnonymous && !strings.HasSuffix(outer.GoType, "*"+derefType):
  1559. outer.GoType = strings.TrimSuffix(outer.GoType, derefType) + "*" + derefType
  1560. }
  1561. }
  1562. // buildMapOfNullable equalizes the nullablity status for aliased and anonymous maps of simple things,
  1563. // with the nullability of its innermost element.
  1564. //
  1565. // NOTE: at the moment, we decide to align the type of the outer element (map) to the type of the inner element
  1566. // The opposite could be done and result in non nullable primitive elements. If we do so, the validation
  1567. // code needs to be adapted by removing IsZero() and Required() calls in codegen.
  1568. func (sg *schemaGenContext) buildMapOfNullable(sch *GenSchema) {
  1569. outer := &sg.GenSchema
  1570. if sch == nil {
  1571. sch = outer
  1572. }
  1573. if sch.IsMap && (outer.IsAliased || outer.IsAnonymous) {
  1574. elem := sch.AdditionalProperties
  1575. for elem != nil {
  1576. if elem.IsPrimitive && elem.IsNullable {
  1577. sg.checkNeedsPointer(outer, nil, elem)
  1578. } else if elem.IsArray {
  1579. // override nullability of array of primitive elements:
  1580. // render element of aliased or anonyous map as a pointer
  1581. it := elem.Items
  1582. for it != nil {
  1583. if it.IsPrimitive && it.IsNullable {
  1584. sg.checkNeedsPointer(outer, sch, it)
  1585. } else if it.IsMap {
  1586. sg.buildMapOfNullable(it)
  1587. }
  1588. it = it.Items
  1589. }
  1590. }
  1591. elem = elem.AdditionalProperties
  1592. }
  1593. }
  1594. }
  1595. func (sg *schemaGenContext) makeGenSchema() error {
  1596. debugLogAsJSON("making gen schema (anon: %t, req: %t, tuple: %t) %s\n",
  1597. !sg.Named, sg.Required, sg.IsTuple, sg.Name, sg.Schema)
  1598. ex := ""
  1599. if sg.Schema.Example != nil {
  1600. ex = fmt.Sprintf("%#v", sg.Schema.Example)
  1601. }
  1602. sg.GenSchema.IsExported = true
  1603. sg.GenSchema.Example = ex
  1604. sg.GenSchema.Path = sg.Path
  1605. sg.GenSchema.IndexVar = sg.IndexVar
  1606. sg.GenSchema.Location = body
  1607. sg.GenSchema.ValueExpression = sg.ValueExpr
  1608. sg.GenSchema.KeyVar = sg.KeyVar
  1609. sg.GenSchema.OriginalName = sg.Name
  1610. sg.GenSchema.Name = sg.GoName()
  1611. sg.GenSchema.Title = sg.Schema.Title
  1612. sg.GenSchema.Description = trimBOM(sg.Schema.Description)
  1613. sg.GenSchema.ReceiverName = sg.Receiver
  1614. sg.GenSchema.sharedValidations = sg.schemaValidations()
  1615. sg.GenSchema.ReadOnly = sg.Schema.ReadOnly
  1616. sg.GenSchema.IncludeValidator = sg.IncludeValidator
  1617. sg.GenSchema.IncludeModel = sg.IncludeModel
  1618. sg.GenSchema.StrictAdditionalProperties = sg.StrictAdditionalProperties
  1619. sg.GenSchema.Default = sg.Schema.Default
  1620. var err error
  1621. returns, err := sg.shortCircuitNamedRef()
  1622. if err != nil {
  1623. return err
  1624. }
  1625. if returns {
  1626. return nil
  1627. }
  1628. debugLogAsJSON("after short circuit named ref", sg.Schema)
  1629. if e := sg.liftSpecialAllOf(); e != nil {
  1630. return e
  1631. }
  1632. nullableOverride := sg.GenSchema.IsNullable
  1633. debugLogAsJSON("after lifting special all of", sg.Schema)
  1634. if sg.Container == "" {
  1635. sg.Container = sg.GenSchema.Name
  1636. }
  1637. if e := sg.buildAllOf(); e != nil {
  1638. return e
  1639. }
  1640. var tpe resolvedType
  1641. if sg.Untyped {
  1642. tpe, err = sg.TypeResolver.ResolveSchema(nil, !sg.Named, sg.IsTuple || sg.Required || sg.GenSchema.Required)
  1643. } else {
  1644. tpe, err = sg.TypeResolver.ResolveSchema(&sg.Schema, !sg.Named, sg.IsTuple || sg.Required || sg.GenSchema.Required)
  1645. }
  1646. if err != nil {
  1647. return err
  1648. }
  1649. debugLog("gschema rrequired: %t, nullable: %t", sg.GenSchema.Required, sg.GenSchema.IsNullable)
  1650. tpe.IsNullable = tpe.IsNullable || nullableOverride
  1651. sg.GenSchema.resolvedType = tpe
  1652. sg.GenSchema.IsBaseType = tpe.IsBaseType
  1653. sg.GenSchema.HasDiscriminator = tpe.HasDiscriminator
  1654. // include format validations, excluding binary
  1655. sg.GenSchema.HasValidations = sg.GenSchema.HasValidations || (tpe.IsCustomFormatter && !tpe.IsStream) || (tpe.IsArray && tpe.ElemType != nil && tpe.ElemType.IsCustomFormatter && !tpe.ElemType.IsStream)
  1656. // usage of a polymorphic base type is rendered with getter funcs on private properties.
  1657. // In the case of aliased types, the value expression remains unchanged to the receiver.
  1658. if tpe.IsArray && tpe.ElemType != nil && tpe.ElemType.IsBaseType && sg.GenSchema.ValueExpression != sg.GenSchema.ReceiverName {
  1659. sg.GenSchema.ValueExpression += asMethod
  1660. }
  1661. debugLog("gschema nullable: %t", sg.GenSchema.IsNullable)
  1662. if e := sg.buildAdditionalProperties(); e != nil {
  1663. return e
  1664. }
  1665. // rewrite value expression from top-down
  1666. cur := &sg.GenSchema
  1667. for cur.AdditionalProperties != nil {
  1668. cur.AdditionalProperties.ValueExpression = cur.ValueExpression + "[" + cur.AdditionalProperties.KeyVar + "]"
  1669. cur = cur.AdditionalProperties
  1670. }
  1671. prev := sg.GenSchema
  1672. if sg.Untyped {
  1673. debugLogAsJSON("untyped resolve:%t", sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required, sg.Schema)
  1674. tpe, err = sg.TypeResolver.ResolveSchema(nil, !sg.Named, sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required)
  1675. } else {
  1676. debugLogAsJSON("typed resolve, isAnonymous(%t), n: %t, t: %t, sgr: %t, sr: %t, isRequired(%t), BaseType(%t)",
  1677. !sg.Named, sg.Named, sg.IsTuple, sg.Required, sg.GenSchema.Required,
  1678. sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required, sg.GenSchema.IsBaseType, sg.Schema)
  1679. tpe, err = sg.TypeResolver.ResolveSchema(&sg.Schema, !sg.Named, sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required)
  1680. }
  1681. if err != nil {
  1682. return err
  1683. }
  1684. otn := tpe.IsNullable // for debug only
  1685. tpe.IsNullable = tpe.IsNullable || nullableOverride
  1686. sg.GenSchema.resolvedType = tpe
  1687. sg.GenSchema.IsComplexObject = prev.IsComplexObject
  1688. sg.GenSchema.IsMap = prev.IsMap
  1689. sg.GenSchema.IsAdditionalProperties = prev.IsAdditionalProperties
  1690. sg.GenSchema.IsBaseType = sg.GenSchema.HasDiscriminator
  1691. debugLogAsJSON("gschema nnullable:IsNullable:%t,resolver.IsNullable:%t,nullableOverride:%t",
  1692. sg.GenSchema.IsNullable, otn, nullableOverride, sg.Schema)
  1693. if err := sg.buildProperties(); err != nil {
  1694. return err
  1695. }
  1696. if err := sg.buildXMLName(); err != nil {
  1697. return err
  1698. }
  1699. if err := sg.buildAdditionalItems(); err != nil {
  1700. return err
  1701. }
  1702. if err := sg.buildItems(); err != nil {
  1703. return err
  1704. }
  1705. if err := sg.buildAliased(); err != nil {
  1706. return err
  1707. }
  1708. sg.buildMapOfNullable(nil)
  1709. debugLog("finished gen schema for %q", sg.Name)
  1710. return nil
  1711. }