parser.go 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608
  1. package swag
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "go/ast"
  8. "go/build"
  9. goparser "go/parser"
  10. "go/token"
  11. "log"
  12. "net/http"
  13. "net/url"
  14. "os"
  15. "os/exec"
  16. "path/filepath"
  17. "reflect"
  18. "sort"
  19. "strconv"
  20. "strings"
  21. "github.com/KyleBanks/depth"
  22. "github.com/go-openapi/spec"
  23. )
  24. const (
  25. // CamelCase indicates using CamelCase strategy for struct field.
  26. CamelCase = "camelcase"
  27. // PascalCase indicates using PascalCase strategy for struct field.
  28. PascalCase = "pascalcase"
  29. // SnakeCase indicates using SnakeCase strategy for struct field.
  30. SnakeCase = "snakecase"
  31. idAttr = "@id"
  32. acceptAttr = "@accept"
  33. produceAttr = "@produce"
  34. paramAttr = "@param"
  35. successAttr = "@success"
  36. failureAttr = "@failure"
  37. responseAttr = "@response"
  38. headerAttr = "@header"
  39. tagsAttr = "@tags"
  40. routerAttr = "@router"
  41. summaryAttr = "@summary"
  42. deprecatedAttr = "@deprecated"
  43. securityAttr = "@security"
  44. titleAttr = "@title"
  45. conNameAttr = "@contact.name"
  46. conURLAttr = "@contact.url"
  47. conEmailAttr = "@contact.email"
  48. licNameAttr = "@license.name"
  49. licURLAttr = "@license.url"
  50. versionAttr = "@version"
  51. descriptionAttr = "@description"
  52. descriptionMarkdownAttr = "@description.markdown"
  53. secBasicAttr = "@securitydefinitions.basic"
  54. secAPIKeyAttr = "@securitydefinitions.apikey"
  55. secApplicationAttr = "@securitydefinitions.oauth2.application"
  56. secImplicitAttr = "@securitydefinitions.oauth2.implicit"
  57. secPasswordAttr = "@securitydefinitions.oauth2.password"
  58. secAccessCodeAttr = "@securitydefinitions.oauth2.accesscode"
  59. tosAttr = "@termsofservice"
  60. xCodeSamplesAttr = "@x-codesamples"
  61. scopeAttrPrefix = "@scope."
  62. )
  63. var (
  64. // ErrRecursiveParseStruct recursively parsing struct.
  65. ErrRecursiveParseStruct = errors.New("recursively parsing struct")
  66. // ErrFuncTypeField field type is func.
  67. ErrFuncTypeField = errors.New("field type is func")
  68. // ErrFailedConvertPrimitiveType Failed to convert for swag to interpretable type.
  69. ErrFailedConvertPrimitiveType = errors.New("swag property: failed convert primitive type")
  70. // ErrSkippedField .swaggo specifies field should be skipped.
  71. ErrSkippedField = errors.New("field is skipped by global overrides")
  72. )
  73. var allMethod = map[string]struct{}{
  74. http.MethodGet: {},
  75. http.MethodPut: {},
  76. http.MethodPost: {},
  77. http.MethodDelete: {},
  78. http.MethodOptions: {},
  79. http.MethodHead: {},
  80. http.MethodPatch: {},
  81. }
  82. // Parser implements a parser for Go source files.
  83. type Parser struct {
  84. // swagger represents the root document object for the API specification
  85. swagger *spec.Swagger
  86. // packages store entities of APIs, definitions, file, package path etc. and their relations
  87. packages *PackagesDefinitions
  88. // parsedSchemas store schemas which have been parsed from ast.TypeSpec
  89. parsedSchemas map[*TypeSpecDef]*Schema
  90. // outputSchemas store schemas which will be export to swagger
  91. outputSchemas map[*TypeSpecDef]*Schema
  92. // existSchemaNames store names of models for conflict determination
  93. existSchemaNames map[string]*Schema
  94. // toBeRenamedSchemas names of models to be renamed
  95. toBeRenamedSchemas map[string]string
  96. // toBeRenamedSchemas URLs of ref models to be renamed
  97. toBeRenamedRefURLs []*url.URL
  98. // PropNamingStrategy naming strategy
  99. PropNamingStrategy string
  100. // ParseVendor parse vendor folder
  101. ParseVendor bool
  102. // ParseDependencies whether swag should be parse outside dependency folder
  103. ParseDependency bool
  104. // ParseInternal whether swag should parse internal packages
  105. ParseInternal bool
  106. // Strict whether swag should error or warn when it detects cases which are most likely user errors
  107. Strict bool
  108. // RequiredByDefault set validation required for all fields by default
  109. RequiredByDefault bool
  110. // structStack stores full names of the structures that were already parsed or are being parsed now
  111. structStack []*TypeSpecDef
  112. // markdownFileDir holds the path to the folder, where markdown files are stored
  113. markdownFileDir string
  114. // codeExampleFilesDir holds path to the folder, where code example files are stored
  115. codeExampleFilesDir string
  116. // collectionFormatInQuery set the default collectionFormat otherwise then 'csv' for array in query params
  117. collectionFormatInQuery string
  118. // excludes excludes dirs and files in SearchDir
  119. excludes map[string]struct{}
  120. // debugging output goes here
  121. debug Debugger
  122. // fieldParserFactory create FieldParser
  123. fieldParserFactory FieldParserFactory
  124. // Overrides allows global replacements of types. A blank replacement will be skipped.
  125. Overrides map[string]string
  126. // parseGoList whether swag use go list to parse dependency
  127. parseGoList bool
  128. }
  129. // FieldParserFactory create FieldParser.
  130. type FieldParserFactory func(ps *Parser, field *ast.Field) FieldParser
  131. // FieldParser parse struct field.
  132. type FieldParser interface {
  133. ShouldSkip() bool
  134. FieldName() (string, error)
  135. CustomSchema() (*spec.Schema, error)
  136. ComplementSchema(schema *spec.Schema) error
  137. IsRequired() (bool, error)
  138. }
  139. // Debugger is the interface that wraps the basic Printf method.
  140. type Debugger interface {
  141. Printf(format string, v ...interface{})
  142. }
  143. // New creates a new Parser with default properties.
  144. func New(options ...func(*Parser)) *Parser {
  145. parser := &Parser{
  146. swagger: &spec.Swagger{
  147. SwaggerProps: spec.SwaggerProps{
  148. Info: &spec.Info{
  149. InfoProps: spec.InfoProps{
  150. Contact: &spec.ContactInfo{},
  151. License: nil,
  152. },
  153. VendorExtensible: spec.VendorExtensible{
  154. Extensions: spec.Extensions{},
  155. },
  156. },
  157. Paths: &spec.Paths{
  158. Paths: make(map[string]spec.PathItem),
  159. VendorExtensible: spec.VendorExtensible{
  160. Extensions: nil,
  161. },
  162. },
  163. Definitions: make(map[string]spec.Schema),
  164. SecurityDefinitions: make(map[string]*spec.SecurityScheme),
  165. },
  166. VendorExtensible: spec.VendorExtensible{
  167. Extensions: nil,
  168. },
  169. },
  170. packages: NewPackagesDefinitions(),
  171. debug: log.New(os.Stdout, "", log.LstdFlags),
  172. parsedSchemas: make(map[*TypeSpecDef]*Schema),
  173. outputSchemas: make(map[*TypeSpecDef]*Schema),
  174. existSchemaNames: make(map[string]*Schema),
  175. toBeRenamedSchemas: make(map[string]string),
  176. excludes: make(map[string]struct{}),
  177. fieldParserFactory: newTagBaseFieldParser,
  178. Overrides: make(map[string]string),
  179. }
  180. for _, option := range options {
  181. option(parser)
  182. }
  183. return parser
  184. }
  185. // SetMarkdownFileDirectory sets the directory to search for markdown files.
  186. func SetMarkdownFileDirectory(directoryPath string) func(*Parser) {
  187. return func(p *Parser) {
  188. p.markdownFileDir = directoryPath
  189. }
  190. }
  191. // SetCodeExamplesDirectory sets the directory to search for code example files.
  192. func SetCodeExamplesDirectory(directoryPath string) func(*Parser) {
  193. return func(p *Parser) {
  194. p.codeExampleFilesDir = directoryPath
  195. }
  196. }
  197. // SetExcludedDirsAndFiles sets directories and files to be excluded when searching.
  198. func SetExcludedDirsAndFiles(excludes string) func(*Parser) {
  199. return func(p *Parser) {
  200. for _, f := range strings.Split(excludes, ",") {
  201. f = strings.TrimSpace(f)
  202. if f != "" {
  203. f = filepath.Clean(f)
  204. p.excludes[f] = struct{}{}
  205. }
  206. }
  207. }
  208. }
  209. // SetStrict sets whether swag should error or warn when it detects cases which are most likely user errors.
  210. func SetStrict(strict bool) func(*Parser) {
  211. return func(p *Parser) {
  212. p.Strict = strict
  213. }
  214. }
  215. // SetDebugger allows the use of user-defined implementations.
  216. func SetDebugger(logger Debugger) func(parser *Parser) {
  217. return func(p *Parser) {
  218. if logger != nil {
  219. p.debug = logger
  220. }
  221. }
  222. }
  223. // SetFieldParserFactory allows the use of user-defined implementations.
  224. func SetFieldParserFactory(factory FieldParserFactory) func(parser *Parser) {
  225. return func(p *Parser) {
  226. p.fieldParserFactory = factory
  227. }
  228. }
  229. // SetOverrides allows the use of user-defined global type overrides.
  230. func SetOverrides(overrides map[string]string) func(parser *Parser) {
  231. return func(p *Parser) {
  232. for k, v := range overrides {
  233. p.Overrides[k] = v
  234. }
  235. }
  236. }
  237. // ParseUsingGoList sets whether swag use go list to parse dependency
  238. func ParseUsingGoList(enabled bool) func(parser *Parser) {
  239. return func(p *Parser) {
  240. p.parseGoList = enabled
  241. }
  242. }
  243. // ParseAPI parses general api info for given searchDir and mainAPIFile.
  244. func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string, parseDepth int) error {
  245. return parser.ParseAPIMultiSearchDir([]string{searchDir}, mainAPIFile, parseDepth)
  246. }
  247. // ParseAPIMultiSearchDir is like ParseAPI but for multiple search dirs.
  248. func (parser *Parser) ParseAPIMultiSearchDir(searchDirs []string, mainAPIFile string, parseDepth int) error {
  249. for _, searchDir := range searchDirs {
  250. parser.debug.Printf("Generate general API Info, search dir:%s", searchDir)
  251. packageDir, err := getPkgName(searchDir)
  252. if err != nil {
  253. parser.debug.Printf("warning: failed to get package name in dir: %s, error: %s", searchDir, err.Error())
  254. }
  255. err = parser.getAllGoFileInfo(packageDir, searchDir)
  256. if err != nil {
  257. return err
  258. }
  259. }
  260. absMainAPIFilePath, err := filepath.Abs(filepath.Join(searchDirs[0], mainAPIFile))
  261. if err != nil {
  262. return err
  263. }
  264. // Use 'go list' command instead of depth.Resolve()
  265. if parser.ParseDependency {
  266. if parser.parseGoList {
  267. pkgs, err := listPackages(context.Background(), filepath.Dir(absMainAPIFilePath), nil, "-deps")
  268. if err != nil {
  269. return fmt.Errorf("pkg %s cannot find all dependencies, %s", filepath.Dir(absMainAPIFilePath), err)
  270. }
  271. length := len(pkgs)
  272. for i := 0; i < length; i++ {
  273. err := parser.getAllGoFileInfoFromDepsByList(pkgs[i])
  274. if err != nil {
  275. return err
  276. }
  277. }
  278. } else {
  279. var t depth.Tree
  280. t.ResolveInternal = true
  281. t.MaxDepth = parseDepth
  282. pkgName, err := getPkgName(filepath.Dir(absMainAPIFilePath))
  283. if err != nil {
  284. return err
  285. }
  286. err = t.Resolve(pkgName)
  287. if err != nil {
  288. return fmt.Errorf("pkg %s cannot find all dependencies, %s", pkgName, err)
  289. }
  290. for i := 0; i < len(t.Root.Deps); i++ {
  291. err := parser.getAllGoFileInfoFromDeps(&t.Root.Deps[i])
  292. if err != nil {
  293. return err
  294. }
  295. }
  296. }
  297. }
  298. err = parser.ParseGeneralAPIInfo(absMainAPIFilePath)
  299. if err != nil {
  300. return err
  301. }
  302. parser.parsedSchemas, err = parser.packages.ParseTypes()
  303. if err != nil {
  304. return err
  305. }
  306. err = rangeFiles(parser.packages.files, parser.ParseRouterAPIInfo)
  307. if err != nil {
  308. return err
  309. }
  310. parser.renameRefSchemas()
  311. return parser.checkOperationIDUniqueness()
  312. }
  313. func getPkgName(searchDir string) (string, error) {
  314. cmd := exec.Command("go", "list", "-f={{.ImportPath}}")
  315. cmd.Dir = searchDir
  316. var stdout, stderr strings.Builder
  317. cmd.Stdout = &stdout
  318. cmd.Stderr = &stderr
  319. if err := cmd.Run(); err != nil {
  320. return "", fmt.Errorf("execute go list command, %s, stdout:%s, stderr:%s", err, stdout.String(), stderr.String())
  321. }
  322. outStr, _ := stdout.String(), stderr.String()
  323. if outStr[0] == '_' { // will shown like _/{GOPATH}/src/{YOUR_PACKAGE} when NOT enable GO MODULE.
  324. outStr = strings.TrimPrefix(outStr, "_"+build.Default.GOPATH+"/src/")
  325. }
  326. f := strings.Split(outStr, "\n")
  327. outStr = f[0]
  328. return outStr, nil
  329. }
  330. // ParseGeneralAPIInfo parses general api info for given mainAPIFile path.
  331. func (parser *Parser) ParseGeneralAPIInfo(mainAPIFile string) error {
  332. fileTree, err := goparser.ParseFile(token.NewFileSet(), mainAPIFile, nil, goparser.ParseComments)
  333. if err != nil {
  334. return fmt.Errorf("cannot parse source files %s: %s", mainAPIFile, err)
  335. }
  336. parser.swagger.Swagger = "2.0"
  337. for _, comment := range fileTree.Comments {
  338. comments := strings.Split(comment.Text(), "\n")
  339. if !isGeneralAPIComment(comments) {
  340. continue
  341. }
  342. err = parseGeneralAPIInfo(parser, comments)
  343. if err != nil {
  344. return err
  345. }
  346. }
  347. return nil
  348. }
  349. func parseGeneralAPIInfo(parser *Parser, comments []string) error {
  350. previousAttribute := ""
  351. // parsing classic meta data model
  352. for line := 0; line < len(comments); line++ {
  353. commentLine := comments[line]
  354. attribute := strings.Split(commentLine, " ")[0]
  355. value := strings.TrimSpace(commentLine[len(attribute):])
  356. multilineBlock := false
  357. if previousAttribute == attribute {
  358. multilineBlock = true
  359. }
  360. switch attr := strings.ToLower(attribute); attr {
  361. case versionAttr, titleAttr, tosAttr, licNameAttr, licURLAttr, conNameAttr, conURLAttr, conEmailAttr:
  362. setSwaggerInfo(parser.swagger, attr, value)
  363. case descriptionAttr:
  364. if multilineBlock {
  365. parser.swagger.Info.Description += "\n" + value
  366. continue
  367. }
  368. setSwaggerInfo(parser.swagger, attr, value)
  369. case descriptionMarkdownAttr:
  370. commentInfo, err := getMarkdownForTag("api", parser.markdownFileDir)
  371. if err != nil {
  372. return err
  373. }
  374. setSwaggerInfo(parser.swagger, descriptionAttr, string(commentInfo))
  375. case "@host":
  376. parser.swagger.Host = value
  377. case "@basepath":
  378. parser.swagger.BasePath = value
  379. case acceptAttr:
  380. err := parser.ParseAcceptComment(value)
  381. if err != nil {
  382. return err
  383. }
  384. case produceAttr:
  385. err := parser.ParseProduceComment(value)
  386. if err != nil {
  387. return err
  388. }
  389. case "@schemes":
  390. parser.swagger.Schemes = strings.Split(value, " ")
  391. case "@tag.name":
  392. parser.swagger.Tags = append(parser.swagger.Tags, spec.Tag{
  393. TagProps: spec.TagProps{
  394. Name: value,
  395. },
  396. })
  397. case "@tag.description":
  398. tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
  399. tag.TagProps.Description = value
  400. replaceLastTag(parser.swagger.Tags, tag)
  401. case "@tag.description.markdown":
  402. tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
  403. commentInfo, err := getMarkdownForTag(tag.TagProps.Name, parser.markdownFileDir)
  404. if err != nil {
  405. return err
  406. }
  407. tag.TagProps.Description = string(commentInfo)
  408. replaceLastTag(parser.swagger.Tags, tag)
  409. case "@tag.docs.url":
  410. tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
  411. tag.TagProps.ExternalDocs = &spec.ExternalDocumentation{
  412. URL: value,
  413. Description: "",
  414. }
  415. replaceLastTag(parser.swagger.Tags, tag)
  416. case "@tag.docs.description":
  417. tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
  418. if tag.TagProps.ExternalDocs == nil {
  419. return fmt.Errorf("%s needs to come after a @tags.docs.url", attribute)
  420. }
  421. tag.TagProps.ExternalDocs.Description = value
  422. replaceLastTag(parser.swagger.Tags, tag)
  423. case secBasicAttr, secAPIKeyAttr, secApplicationAttr, secImplicitAttr, secPasswordAttr, secAccessCodeAttr:
  424. scheme, err := parseSecAttributes(attribute, comments, &line)
  425. if err != nil {
  426. return err
  427. }
  428. parser.swagger.SecurityDefinitions[value] = scheme
  429. case "@query.collection.format":
  430. parser.collectionFormatInQuery = value
  431. default:
  432. prefixExtension := "@x-"
  433. // Prefix extension + 1 char + 1 space + 1 char
  434. if len(attribute) > 5 && attribute[:len(prefixExtension)] == prefixExtension {
  435. extExistsInSecurityDef := false
  436. // for each security definition
  437. for _, v := range parser.swagger.SecurityDefinitions {
  438. // check if extension exists
  439. _, extExistsInSecurityDef = v.VendorExtensible.Extensions.GetString(attribute[1:])
  440. // if it exists in at least one, then we stop iterating
  441. if extExistsInSecurityDef {
  442. break
  443. }
  444. }
  445. // if it is present on security def, don't add it again
  446. if extExistsInSecurityDef {
  447. break
  448. }
  449. var valueJSON interface{}
  450. split := strings.SplitAfter(commentLine, attribute+" ")
  451. if len(split) < 2 {
  452. return fmt.Errorf("annotation %s need a value", attribute)
  453. }
  454. extensionName := "x-" + strings.SplitAfter(attribute, prefixExtension)[1]
  455. err := json.Unmarshal([]byte(split[1]), &valueJSON)
  456. if err != nil {
  457. return fmt.Errorf("annotation %s need a valid json value", attribute)
  458. }
  459. if strings.Contains(extensionName, "logo") {
  460. parser.swagger.Info.Extensions.Add(extensionName, valueJSON)
  461. } else {
  462. if parser.swagger.Extensions == nil {
  463. parser.swagger.Extensions = make(map[string]interface{})
  464. }
  465. parser.swagger.Extensions[attribute[1:]] = valueJSON
  466. }
  467. }
  468. }
  469. previousAttribute = attribute
  470. }
  471. return nil
  472. }
  473. func setSwaggerInfo(swagger *spec.Swagger, attribute, value string) {
  474. switch attribute {
  475. case versionAttr:
  476. swagger.Info.Version = value
  477. case titleAttr:
  478. swagger.Info.Title = value
  479. case tosAttr:
  480. swagger.Info.TermsOfService = value
  481. case descriptionAttr:
  482. swagger.Info.Description = value
  483. case conNameAttr:
  484. swagger.Info.Contact.Name = value
  485. case conEmailAttr:
  486. swagger.Info.Contact.Email = value
  487. case conURLAttr:
  488. swagger.Info.Contact.URL = value
  489. case licNameAttr:
  490. swagger.Info.License = initIfEmpty(swagger.Info.License)
  491. swagger.Info.License.Name = value
  492. case licURLAttr:
  493. swagger.Info.License = initIfEmpty(swagger.Info.License)
  494. swagger.Info.License.URL = value
  495. }
  496. }
  497. func parseSecAttributes(context string, lines []string, index *int) (*spec.SecurityScheme, error) {
  498. const (
  499. in = "@in"
  500. name = "@name"
  501. descriptionAttr = "@description"
  502. tokenURL = "@tokenurl"
  503. authorizationURL = "@authorizationurl"
  504. )
  505. var search []string
  506. attribute := strings.ToLower(strings.Split(lines[*index], " ")[0])
  507. switch attribute {
  508. case secBasicAttr:
  509. return spec.BasicAuth(), nil
  510. case secAPIKeyAttr:
  511. search = []string{in, name}
  512. case secApplicationAttr, secPasswordAttr:
  513. search = []string{tokenURL}
  514. case secImplicitAttr:
  515. search = []string{authorizationURL}
  516. case secAccessCodeAttr:
  517. search = []string{tokenURL, authorizationURL}
  518. }
  519. // For the first line we get the attributes in the context parameter, so we skip to the next one
  520. *index++
  521. attrMap, scopes := make(map[string]string), make(map[string]string)
  522. extensions, description := make(map[string]interface{}), ""
  523. for ; *index < len(lines); *index++ {
  524. v := lines[*index]
  525. securityAttr := strings.ToLower(strings.Split(v, " ")[0])
  526. for _, findterm := range search {
  527. if securityAttr == findterm {
  528. attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
  529. continue
  530. }
  531. }
  532. isExists, err := isExistsScope(securityAttr)
  533. if err != nil {
  534. return nil, err
  535. }
  536. if isExists {
  537. scopes[securityAttr[len(scopeAttrPrefix):]] = v[len(securityAttr):]
  538. }
  539. if strings.HasPrefix(securityAttr, "@x-") {
  540. // Add the custom attribute without the @
  541. extensions[securityAttr[1:]] = strings.TrimSpace(v[len(securityAttr):])
  542. }
  543. // Not mandatory field
  544. if securityAttr == descriptionAttr {
  545. description = strings.TrimSpace(v[len(securityAttr):])
  546. }
  547. // next securityDefinitions
  548. if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
  549. // Go back to the previous line and break
  550. *index--
  551. break
  552. }
  553. }
  554. if len(attrMap) != len(search) {
  555. return nil, fmt.Errorf("%s is %v required", context, search)
  556. }
  557. var scheme *spec.SecurityScheme
  558. switch attribute {
  559. case secAPIKeyAttr:
  560. scheme = spec.APIKeyAuth(attrMap[name], attrMap[in])
  561. case secApplicationAttr:
  562. scheme = spec.OAuth2Application(attrMap[tokenURL])
  563. case secImplicitAttr:
  564. scheme = spec.OAuth2Implicit(attrMap[authorizationURL])
  565. case secPasswordAttr:
  566. scheme = spec.OAuth2Password(attrMap[tokenURL])
  567. case secAccessCodeAttr:
  568. scheme = spec.OAuth2AccessToken(attrMap[authorizationURL], attrMap[tokenURL])
  569. }
  570. scheme.Description = description
  571. for extKey, extValue := range extensions {
  572. scheme.AddExtension(extKey, extValue)
  573. }
  574. for scope, scopeDescription := range scopes {
  575. scheme.AddScope(scope, scopeDescription)
  576. }
  577. return scheme, nil
  578. }
  579. func initIfEmpty(license *spec.License) *spec.License {
  580. if license == nil {
  581. return new(spec.License)
  582. }
  583. return license
  584. }
  585. // ParseAcceptComment parses comment for given `accept` comment string.
  586. func (parser *Parser) ParseAcceptComment(commentLine string) error {
  587. return parseMimeTypeList(commentLine, &parser.swagger.Consumes, "%v accept type can't be accepted")
  588. }
  589. // ParseProduceComment parses comment for given `produce` comment string.
  590. func (parser *Parser) ParseProduceComment(commentLine string) error {
  591. return parseMimeTypeList(commentLine, &parser.swagger.Produces, "%v produce type can't be accepted")
  592. }
  593. func isGeneralAPIComment(comments []string) bool {
  594. for _, commentLine := range comments {
  595. attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
  596. switch attribute {
  597. // The @summary, @router, @success, @failure annotation belongs to Operation
  598. case summaryAttr, routerAttr, successAttr, failureAttr, responseAttr:
  599. return false
  600. }
  601. }
  602. return true
  603. }
  604. func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) {
  605. dirEntries, err := os.ReadDir(dirPath)
  606. if err != nil {
  607. return nil, err
  608. }
  609. for _, entry := range dirEntries {
  610. if entry.IsDir() {
  611. continue
  612. }
  613. fileName := entry.Name()
  614. if !strings.Contains(fileName, ".md") {
  615. continue
  616. }
  617. if strings.Contains(fileName, tagName) {
  618. fullPath := filepath.Join(dirPath, fileName)
  619. commentInfo, err := os.ReadFile(fullPath)
  620. if err != nil {
  621. return nil, fmt.Errorf("Failed to read markdown file %s error: %s ", fullPath, err)
  622. }
  623. return commentInfo, nil
  624. }
  625. }
  626. return nil, fmt.Errorf("Unable to find markdown file for tag %s in the given directory", tagName)
  627. }
  628. func isExistsScope(scope string) (bool, error) {
  629. s := strings.Fields(scope)
  630. for _, v := range s {
  631. if strings.Contains(v, scopeAttrPrefix) {
  632. if strings.Contains(v, ",") {
  633. return false, fmt.Errorf("@scope can't use comma(,) get=" + v)
  634. }
  635. }
  636. }
  637. return strings.Contains(scope, scopeAttrPrefix), nil
  638. }
  639. // ParseRouterAPIInfo parses router api info for given astFile.
  640. func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) error {
  641. for _, astDescription := range astFile.Decls {
  642. astDeclaration, ok := astDescription.(*ast.FuncDecl)
  643. if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
  644. // for per 'function' comment, create a new 'Operation' object
  645. operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir))
  646. for _, comment := range astDeclaration.Doc.List {
  647. err := operation.ParseComment(comment.Text, astFile)
  648. if err != nil {
  649. return fmt.Errorf("ParseComment error in file %s :%+v", fileName, err)
  650. }
  651. }
  652. err := processRouterOperation(parser, operation)
  653. if err != nil {
  654. return err
  655. }
  656. }
  657. }
  658. return nil
  659. }
  660. func refRouteMethodOp(item *spec.PathItem, method string) (op **spec.Operation) {
  661. switch method {
  662. case http.MethodGet:
  663. op = &item.Get
  664. case http.MethodPost:
  665. op = &item.Post
  666. case http.MethodDelete:
  667. op = &item.Delete
  668. case http.MethodPut:
  669. op = &item.Put
  670. case http.MethodPatch:
  671. op = &item.Patch
  672. case http.MethodHead:
  673. op = &item.Head
  674. case http.MethodOptions:
  675. op = &item.Options
  676. }
  677. return
  678. }
  679. func processRouterOperation(parser *Parser, operation *Operation) error {
  680. for _, routeProperties := range operation.RouterProperties {
  681. var (
  682. pathItem spec.PathItem
  683. ok bool
  684. )
  685. pathItem, ok = parser.swagger.Paths.Paths[routeProperties.Path]
  686. if !ok {
  687. pathItem = spec.PathItem{}
  688. }
  689. op := refRouteMethodOp(&pathItem, routeProperties.HTTPMethod)
  690. // check if we already have an operation for this path and method
  691. if *op != nil {
  692. err := fmt.Errorf("route %s %s is declared multiple times", routeProperties.HTTPMethod, routeProperties.Path)
  693. if parser.Strict {
  694. return err
  695. }
  696. parser.debug.Printf("warning: %s\n", err)
  697. }
  698. *op = &operation.Operation
  699. parser.swagger.Paths.Paths[routeProperties.Path] = pathItem
  700. }
  701. return nil
  702. }
  703. func convertFromSpecificToPrimitive(typeName string) (string, error) {
  704. name := typeName
  705. if strings.ContainsRune(name, '.') {
  706. name = strings.Split(name, ".")[1]
  707. }
  708. switch strings.ToUpper(name) {
  709. case "TIME", "OBJECTID", "UUID":
  710. return STRING, nil
  711. case "DECIMAL":
  712. return NUMBER, nil
  713. }
  714. return typeName, ErrFailedConvertPrimitiveType
  715. }
  716. func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (*spec.Schema, error) {
  717. if override, ok := parser.Overrides[typeName]; ok {
  718. parser.debug.Printf("Override detected for %s: using %s instead", typeName, override)
  719. return parseObjectSchema(parser, override, file)
  720. }
  721. if IsInterfaceLike(typeName) {
  722. return &spec.Schema{}, nil
  723. }
  724. if IsGolangPrimitiveType(typeName) {
  725. return PrimitiveSchema(TransToValidSchemeType(typeName)), nil
  726. }
  727. schemaType, err := convertFromSpecificToPrimitive(typeName)
  728. if err == nil {
  729. return PrimitiveSchema(schemaType), nil
  730. }
  731. typeSpecDef := parser.packages.FindTypeSpec(typeName, file, parser.ParseDependency)
  732. if typeSpecDef == nil {
  733. return nil, fmt.Errorf("cannot find type definition: %s", typeName)
  734. }
  735. if override, ok := parser.Overrides[typeSpecDef.FullPath()]; ok {
  736. if override == "" {
  737. parser.debug.Printf("Override detected for %s: ignoring", typeSpecDef.FullPath())
  738. return nil, ErrSkippedField
  739. }
  740. parser.debug.Printf("Override detected for %s: using %s instead", typeSpecDef.FullPath(), override)
  741. separator := strings.LastIndex(override, ".")
  742. if separator == -1 {
  743. // treat as a swaggertype tag
  744. parts := strings.Split(override, ",")
  745. return BuildCustomSchema(parts)
  746. }
  747. typeSpecDef = parser.packages.findTypeSpec(override[0:separator], override[separator+1:])
  748. }
  749. schema, ok := parser.parsedSchemas[typeSpecDef]
  750. if !ok {
  751. var err error
  752. schema, err = parser.ParseDefinition(typeSpecDef)
  753. if err != nil {
  754. if err == ErrRecursiveParseStruct && ref {
  755. return parser.getRefTypeSchema(typeSpecDef, schema), nil
  756. }
  757. return nil, err
  758. }
  759. }
  760. if ref && len(schema.Schema.Type) > 0 && schema.Schema.Type[0] == OBJECT {
  761. return parser.getRefTypeSchema(typeSpecDef, schema), nil
  762. }
  763. return schema.Schema, nil
  764. }
  765. func (parser *Parser) renameRefSchemas() {
  766. if len(parser.toBeRenamedSchemas) == 0 {
  767. return
  768. }
  769. // rename schemas in swagger.Definitions
  770. for name, pkgPath := range parser.toBeRenamedSchemas {
  771. if schema, ok := parser.swagger.Definitions[name]; ok {
  772. delete(parser.swagger.Definitions, name)
  773. name = parser.renameSchema(name, pkgPath)
  774. parser.swagger.Definitions[name] = schema
  775. }
  776. }
  777. // rename URLs if match
  778. for _, refURL := range parser.toBeRenamedRefURLs {
  779. parts := strings.Split(refURL.Fragment, "/")
  780. name := parts[len(parts)-1]
  781. if pkgPath, ok := parser.toBeRenamedSchemas[name]; ok {
  782. parts[len(parts)-1] = parser.renameSchema(name, pkgPath)
  783. refURL.Fragment = strings.Join(parts, "/")
  784. }
  785. }
  786. }
  787. func (parser *Parser) renameSchema(name, pkgPath string) string {
  788. parts := strings.Split(name, ".")
  789. name = fullTypeName(pkgPath, parts[len(parts)-1])
  790. name = strings.ReplaceAll(name, "/", "_")
  791. return name
  792. }
  793. func (parser *Parser) getRefTypeSchema(typeSpecDef *TypeSpecDef, schema *Schema) *spec.Schema {
  794. _, ok := parser.outputSchemas[typeSpecDef]
  795. if !ok {
  796. existSchema, ok := parser.existSchemaNames[schema.Name]
  797. if ok {
  798. // store the first one to be renamed after parsing over
  799. _, ok = parser.toBeRenamedSchemas[existSchema.Name]
  800. if !ok {
  801. parser.toBeRenamedSchemas[existSchema.Name] = existSchema.PkgPath
  802. }
  803. // rename not the first one
  804. schema.Name = parser.renameSchema(schema.Name, schema.PkgPath)
  805. } else {
  806. parser.existSchemaNames[schema.Name] = schema
  807. }
  808. parser.swagger.Definitions[schema.Name] = spec.Schema{}
  809. if schema.Schema != nil {
  810. parser.swagger.Definitions[schema.Name] = *schema.Schema
  811. }
  812. parser.outputSchemas[typeSpecDef] = schema
  813. }
  814. refSchema := RefSchema(schema.Name)
  815. // store every URL
  816. parser.toBeRenamedRefURLs = append(parser.toBeRenamedRefURLs, refSchema.Ref.GetURL())
  817. return refSchema
  818. }
  819. func (parser *Parser) isInStructStack(typeSpecDef *TypeSpecDef) bool {
  820. for _, specDef := range parser.structStack {
  821. if typeSpecDef == specDef {
  822. return true
  823. }
  824. }
  825. return false
  826. }
  827. // ParseDefinition parses given type spec that corresponds to the type under
  828. // given name and package, and populates swagger schema definitions registry
  829. // with a schema for the given type
  830. func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error) {
  831. typeName := typeSpecDef.FullName()
  832. var refTypeName string
  833. if fn, ok := (typeSpecDef.ParentSpec).(*ast.FuncDecl); ok {
  834. refTypeName = TypeDocNameFuncScoped(typeName, typeSpecDef.TypeSpec, fn.Name.Name)
  835. } else {
  836. refTypeName = TypeDocName(typeName, typeSpecDef.TypeSpec)
  837. }
  838. schema, found := parser.parsedSchemas[typeSpecDef]
  839. if found {
  840. parser.debug.Printf("Skipping '%s', already parsed.", typeName)
  841. return schema, nil
  842. }
  843. if parser.isInStructStack(typeSpecDef) {
  844. parser.debug.Printf("Skipping '%s', recursion detected.", typeName)
  845. return &Schema{
  846. Name: refTypeName,
  847. PkgPath: typeSpecDef.PkgPath,
  848. Schema: PrimitiveSchema(OBJECT),
  849. },
  850. ErrRecursiveParseStruct
  851. }
  852. parser.structStack = append(parser.structStack, typeSpecDef)
  853. parser.debug.Printf("Generating %s", typeName)
  854. definition, err := parser.parseTypeExpr(typeSpecDef.File, typeSpecDef.TypeSpec.Type, false)
  855. if err != nil {
  856. return nil, err
  857. }
  858. if definition.Description == "" {
  859. fillDefinitionDescription(definition, typeSpecDef.File, typeSpecDef)
  860. }
  861. sch := Schema{
  862. Name: refTypeName,
  863. PkgPath: typeSpecDef.PkgPath,
  864. Schema: definition,
  865. }
  866. parser.parsedSchemas[typeSpecDef] = &sch
  867. // update an empty schema as a result of recursion
  868. s2, found := parser.outputSchemas[typeSpecDef]
  869. if found {
  870. parser.swagger.Definitions[s2.Name] = *definition
  871. }
  872. return &sch, nil
  873. }
  874. func fullTypeName(pkgName, typeName string) string {
  875. if pkgName != "" && !ignoreNameOverride(typeName) {
  876. return pkgName + "." + typeName
  877. }
  878. return typeName
  879. }
  880. func fullTypeNameFunctionScoped(pkgName, fnName, typeName string) string {
  881. if pkgName != "" {
  882. return pkgName + "." + fnName + "." + typeName
  883. }
  884. return typeName
  885. }
  886. // fillDefinitionDescription additionally fills fields in definition (spec.Schema)
  887. // TODO: If .go file contains many types, it may work for a long time
  888. func fillDefinitionDescription(definition *spec.Schema, file *ast.File, typeSpecDef *TypeSpecDef) {
  889. for _, astDeclaration := range file.Decls {
  890. generalDeclaration, ok := astDeclaration.(*ast.GenDecl)
  891. if !ok || generalDeclaration.Tok != token.TYPE {
  892. continue
  893. }
  894. for _, astSpec := range generalDeclaration.Specs {
  895. typeSpec, ok := astSpec.(*ast.TypeSpec)
  896. if !ok || typeSpec != typeSpecDef.TypeSpec {
  897. continue
  898. }
  899. definition.Description =
  900. extractDeclarationDescription(typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc)
  901. }
  902. }
  903. }
  904. // extractDeclarationDescription gets first description
  905. // from attribute descriptionAttr in commentGroups (ast.CommentGroup)
  906. func extractDeclarationDescription(commentGroups ...*ast.CommentGroup) string {
  907. var description string
  908. for _, commentGroup := range commentGroups {
  909. if commentGroup == nil {
  910. continue
  911. }
  912. isHandlingDescription := false
  913. for _, comment := range commentGroup.List {
  914. commentText := strings.TrimSpace(strings.TrimLeft(comment.Text, "/"))
  915. attribute := strings.Split(commentText, " ")[0]
  916. if strings.ToLower(attribute) != descriptionAttr {
  917. if !isHandlingDescription {
  918. continue
  919. }
  920. break
  921. }
  922. isHandlingDescription = true
  923. description += " " + strings.TrimSpace(commentText[len(attribute):])
  924. }
  925. }
  926. return strings.TrimLeft(description, " ")
  927. }
  928. // parseTypeExpr parses given type expression that corresponds to the type under
  929. // given name and package, and returns swagger schema for it.
  930. func (parser *Parser) parseTypeExpr(file *ast.File, typeExpr ast.Expr, ref bool) (*spec.Schema, error) {
  931. switch expr := typeExpr.(type) {
  932. // type Foo interface{}
  933. case *ast.InterfaceType:
  934. return &spec.Schema{}, nil
  935. // type Foo struct {...}
  936. case *ast.StructType:
  937. return parser.parseStruct(file, expr.Fields)
  938. // type Foo Baz
  939. case *ast.Ident:
  940. return parser.getTypeSchema(expr.Name, file, ref)
  941. // type Foo *Baz
  942. case *ast.StarExpr:
  943. return parser.parseTypeExpr(file, expr.X, ref)
  944. // type Foo pkg.Bar
  945. case *ast.SelectorExpr:
  946. if xIdent, ok := expr.X.(*ast.Ident); ok {
  947. return parser.getTypeSchema(fullTypeName(xIdent.Name, expr.Sel.Name), file, ref)
  948. }
  949. // type Foo []Baz
  950. case *ast.ArrayType:
  951. itemSchema, err := parser.parseTypeExpr(file, expr.Elt, true)
  952. if err != nil {
  953. return nil, err
  954. }
  955. return spec.ArrayProperty(itemSchema), nil
  956. // type Foo map[string]Bar
  957. case *ast.MapType:
  958. if _, ok := expr.Value.(*ast.InterfaceType); ok {
  959. return spec.MapProperty(nil), nil
  960. }
  961. schema, err := parser.parseTypeExpr(file, expr.Value, true)
  962. if err != nil {
  963. return nil, err
  964. }
  965. return spec.MapProperty(schema), nil
  966. case *ast.FuncType:
  967. return nil, ErrFuncTypeField
  968. // ...
  969. }
  970. return parser.parseGenericTypeExpr(file, typeExpr)
  971. }
  972. func (parser *Parser) parseStruct(file *ast.File, fields *ast.FieldList) (*spec.Schema, error) {
  973. required, properties := make([]string, 0), make(map[string]spec.Schema)
  974. for _, field := range fields.List {
  975. fieldProps, requiredFromAnon, err := parser.parseStructField(file, field)
  976. if err != nil {
  977. if err == ErrFuncTypeField || err == ErrSkippedField {
  978. continue
  979. }
  980. return nil, err
  981. }
  982. if len(fieldProps) == 0 {
  983. continue
  984. }
  985. required = append(required, requiredFromAnon...)
  986. for k, v := range fieldProps {
  987. properties[k] = v
  988. }
  989. }
  990. sort.Strings(required)
  991. return &spec.Schema{
  992. SchemaProps: spec.SchemaProps{
  993. Type: []string{OBJECT},
  994. Properties: properties,
  995. Required: required,
  996. },
  997. }, nil
  998. }
  999. func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[string]spec.Schema, []string, error) {
  1000. if field.Names == nil {
  1001. if field.Tag != nil {
  1002. skip, ok := reflect.StructTag(strings.ReplaceAll(field.Tag.Value, "`", "")).Lookup("swaggerignore")
  1003. if ok && strings.EqualFold(skip, "true") {
  1004. return nil, nil, nil
  1005. }
  1006. }
  1007. typeName, err := getFieldType(file, field.Type)
  1008. if err != nil {
  1009. return nil, nil, err
  1010. }
  1011. schema, err := parser.getTypeSchema(typeName, file, false)
  1012. if err != nil {
  1013. return nil, nil, err
  1014. }
  1015. if len(schema.Type) > 0 && schema.Type[0] == OBJECT {
  1016. if len(schema.Properties) == 0 {
  1017. return nil, nil, nil
  1018. }
  1019. properties := map[string]spec.Schema{}
  1020. for k, v := range schema.Properties {
  1021. properties[k] = v
  1022. }
  1023. return properties, schema.SchemaProps.Required, nil
  1024. }
  1025. // for alias type of non-struct types ,such as array,map, etc. ignore field tag.
  1026. return map[string]spec.Schema{typeName: *schema}, nil, nil
  1027. }
  1028. ps := parser.fieldParserFactory(parser, field)
  1029. if ps.ShouldSkip() {
  1030. return nil, nil, nil
  1031. }
  1032. fieldName, err := ps.FieldName()
  1033. if err != nil {
  1034. return nil, nil, err
  1035. }
  1036. schema, err := ps.CustomSchema()
  1037. if err != nil {
  1038. return nil, nil, err
  1039. }
  1040. if schema == nil {
  1041. typeName, err := getFieldType(file, field.Type)
  1042. if err == nil {
  1043. // named type
  1044. schema, err = parser.getTypeSchema(typeName, file, true)
  1045. } else {
  1046. // unnamed type
  1047. schema, err = parser.parseTypeExpr(file, field.Type, false)
  1048. }
  1049. if err != nil {
  1050. return nil, nil, err
  1051. }
  1052. }
  1053. err = ps.ComplementSchema(schema)
  1054. if err != nil {
  1055. return nil, nil, err
  1056. }
  1057. var tagRequired []string
  1058. required, err := ps.IsRequired()
  1059. if err != nil {
  1060. return nil, nil, err
  1061. }
  1062. if required {
  1063. tagRequired = append(tagRequired, fieldName)
  1064. }
  1065. return map[string]spec.Schema{fieldName: *schema}, tagRequired, nil
  1066. }
  1067. func getFieldType(file *ast.File, field ast.Expr) (string, error) {
  1068. switch fieldType := field.(type) {
  1069. case *ast.Ident:
  1070. return fieldType.Name, nil
  1071. case *ast.SelectorExpr:
  1072. packageName, err := getFieldType(file, fieldType.X)
  1073. if err != nil {
  1074. return "", err
  1075. }
  1076. return fullTypeName(packageName, fieldType.Sel.Name), nil
  1077. case *ast.StarExpr:
  1078. fullName, err := getFieldType(file, fieldType.X)
  1079. if err != nil {
  1080. return "", err
  1081. }
  1082. return fullName, nil
  1083. default:
  1084. return getGenericFieldType(file, field, nil)
  1085. }
  1086. }
  1087. // GetSchemaTypePath get path of schema type.
  1088. func (parser *Parser) GetSchemaTypePath(schema *spec.Schema, depth int) []string {
  1089. if schema == nil || depth == 0 {
  1090. return nil
  1091. }
  1092. name := schema.Ref.String()
  1093. if name != "" {
  1094. if pos := strings.LastIndexByte(name, '/'); pos >= 0 {
  1095. name = name[pos+1:]
  1096. if schema, ok := parser.swagger.Definitions[name]; ok {
  1097. return parser.GetSchemaTypePath(&schema, depth)
  1098. }
  1099. }
  1100. return nil
  1101. }
  1102. if len(schema.Type) > 0 {
  1103. switch schema.Type[0] {
  1104. case ARRAY:
  1105. depth--
  1106. s := []string{schema.Type[0]}
  1107. return append(s, parser.GetSchemaTypePath(schema.Items.Schema, depth)...)
  1108. case OBJECT:
  1109. if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
  1110. // for map
  1111. depth--
  1112. s := []string{schema.Type[0]}
  1113. return append(s, parser.GetSchemaTypePath(schema.AdditionalProperties.Schema, depth)...)
  1114. }
  1115. }
  1116. return []string{schema.Type[0]}
  1117. }
  1118. return []string{ANY}
  1119. }
  1120. func replaceLastTag(slice []spec.Tag, element spec.Tag) {
  1121. slice = append(slice[:len(slice)-1], element)
  1122. }
  1123. // defineTypeOfExample example value define the type (object and array unsupported).
  1124. func defineTypeOfExample(schemaType, arrayType, exampleValue string) (interface{}, error) {
  1125. switch schemaType {
  1126. case STRING:
  1127. return exampleValue, nil
  1128. case NUMBER:
  1129. v, err := strconv.ParseFloat(exampleValue, 64)
  1130. if err != nil {
  1131. return nil, fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err)
  1132. }
  1133. return v, nil
  1134. case INTEGER:
  1135. v, err := strconv.Atoi(exampleValue)
  1136. if err != nil {
  1137. return nil, fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err)
  1138. }
  1139. return v, nil
  1140. case BOOLEAN:
  1141. v, err := strconv.ParseBool(exampleValue)
  1142. if err != nil {
  1143. return nil, fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err)
  1144. }
  1145. return v, nil
  1146. case ARRAY:
  1147. values := strings.Split(exampleValue, ",")
  1148. result := make([]interface{}, 0)
  1149. for _, value := range values {
  1150. v, err := defineTypeOfExample(arrayType, "", value)
  1151. if err != nil {
  1152. return nil, err
  1153. }
  1154. result = append(result, v)
  1155. }
  1156. return result, nil
  1157. case OBJECT:
  1158. if arrayType == "" {
  1159. return nil, fmt.Errorf("%s is unsupported type in example value `%s`", schemaType, exampleValue)
  1160. }
  1161. values := strings.Split(exampleValue, ",")
  1162. result := map[string]interface{}{}
  1163. for _, value := range values {
  1164. mapData := strings.Split(value, ":")
  1165. if len(mapData) == 2 {
  1166. v, err := defineTypeOfExample(arrayType, "", mapData[1])
  1167. if err != nil {
  1168. return nil, err
  1169. }
  1170. result[mapData[0]] = v
  1171. continue
  1172. }
  1173. return nil, fmt.Errorf("example value %s should format: key:value", exampleValue)
  1174. }
  1175. return result, nil
  1176. }
  1177. return nil, fmt.Errorf("%s is unsupported type in example value %s", schemaType, exampleValue)
  1178. }
  1179. // GetAllGoFileInfo gets all Go source files information for given searchDir.
  1180. func (parser *Parser) getAllGoFileInfo(packageDir, searchDir string) error {
  1181. return filepath.Walk(searchDir, func(path string, f os.FileInfo, _ error) error {
  1182. err := parser.Skip(path, f)
  1183. if err != nil {
  1184. return err
  1185. }
  1186. if f.IsDir() {
  1187. return nil
  1188. }
  1189. relPath, err := filepath.Rel(searchDir, path)
  1190. if err != nil {
  1191. return err
  1192. }
  1193. return parser.parseFile(filepath.ToSlash(filepath.Dir(filepath.Clean(filepath.Join(packageDir, relPath)))), path, nil)
  1194. })
  1195. }
  1196. func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg) error {
  1197. ignoreInternal := pkg.Internal && !parser.ParseInternal
  1198. if ignoreInternal || !pkg.Resolved { // ignored internal and not resolved dependencies
  1199. return nil
  1200. }
  1201. // Skip cgo
  1202. if pkg.Raw == nil && pkg.Name == "C" {
  1203. return nil
  1204. }
  1205. srcDir := pkg.Raw.Dir
  1206. files, err := os.ReadDir(srcDir) // only parsing files in the dir(don't contain sub dir files)
  1207. if err != nil {
  1208. return err
  1209. }
  1210. for _, f := range files {
  1211. if f.IsDir() {
  1212. continue
  1213. }
  1214. path := filepath.Join(srcDir, f.Name())
  1215. if err := parser.parseFile(pkg.Name, path, nil); err != nil {
  1216. return err
  1217. }
  1218. }
  1219. for i := 0; i < len(pkg.Deps); i++ {
  1220. if err := parser.getAllGoFileInfoFromDeps(&pkg.Deps[i]); err != nil {
  1221. return err
  1222. }
  1223. }
  1224. return nil
  1225. }
  1226. func (parser *Parser) parseFile(packageDir, path string, src interface{}) error {
  1227. if strings.HasSuffix(strings.ToLower(path), "_test.go") || filepath.Ext(path) != ".go" {
  1228. return nil
  1229. }
  1230. // positions are relative to FileSet
  1231. astFile, err := goparser.ParseFile(token.NewFileSet(), path, src, goparser.ParseComments)
  1232. if err != nil {
  1233. return fmt.Errorf("ParseFile error:%+v", err)
  1234. }
  1235. err = parser.packages.CollectAstFile(packageDir, path, astFile)
  1236. if err != nil {
  1237. return err
  1238. }
  1239. return nil
  1240. }
  1241. func (parser *Parser) checkOperationIDUniqueness() error {
  1242. // operationsIds contains all operationId annotations to check it's unique
  1243. operationsIds := make(map[string]string)
  1244. for path, item := range parser.swagger.Paths.Paths {
  1245. var method, id string
  1246. for method = range allMethod {
  1247. op := refRouteMethodOp(&item, method)
  1248. if *op != nil {
  1249. id = (**op).ID
  1250. break
  1251. }
  1252. }
  1253. if id == "" {
  1254. continue
  1255. }
  1256. current := fmt.Sprintf("%s %s", method, path)
  1257. previous, ok := operationsIds[id]
  1258. if ok {
  1259. return fmt.Errorf(
  1260. "duplicated @id annotation '%s' found in '%s', previously declared in: '%s'",
  1261. id, current, previous)
  1262. }
  1263. operationsIds[id] = current
  1264. }
  1265. return nil
  1266. }
  1267. // Skip returns filepath.SkipDir error if match vendor and hidden folder.
  1268. func (parser *Parser) Skip(path string, f os.FileInfo) error {
  1269. return walkWith(parser.excludes, parser.ParseVendor)(path, f)
  1270. }
  1271. func walkWith(excludes map[string]struct{}, parseVendor bool) func(path string, fileInfo os.FileInfo) error {
  1272. return func(path string, f os.FileInfo) error {
  1273. if f.IsDir() {
  1274. if !parseVendor && f.Name() == "vendor" || // ignore "vendor"
  1275. f.Name() == "docs" || // exclude docs
  1276. len(f.Name()) > 1 && f.Name()[0] == '.' && f.Name() != ".." { // exclude all hidden folder
  1277. return filepath.SkipDir
  1278. }
  1279. if excludes != nil {
  1280. if _, ok := excludes[path]; ok {
  1281. return filepath.SkipDir
  1282. }
  1283. }
  1284. }
  1285. return nil
  1286. }
  1287. }
  1288. // GetSwagger returns *spec.Swagger which is the root document object for the API specification.
  1289. func (parser *Parser) GetSwagger() *spec.Swagger {
  1290. return parser.swagger
  1291. }
  1292. // addTestType just for tests.
  1293. func (parser *Parser) addTestType(typename string) {
  1294. typeDef := &TypeSpecDef{}
  1295. parser.packages.uniqueDefinitions[typename] = typeDef
  1296. parser.parsedSchemas[typeDef] = &Schema{
  1297. PkgPath: "",
  1298. Name: typename,
  1299. Schema: PrimitiveSchema(OBJECT),
  1300. }
  1301. }