goai.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. // Package goai implements and provides document generating for OpenApi specification.
  7. //
  8. // https://editor.swagger.io/
  9. package goai
  10. import (
  11. "context"
  12. "fmt"
  13. "reflect"
  14. "github.com/gogf/gf/v2/errors/gcode"
  15. "github.com/gogf/gf/v2/errors/gerror"
  16. "github.com/gogf/gf/v2/internal/intlog"
  17. "github.com/gogf/gf/v2/internal/json"
  18. "github.com/gogf/gf/v2/text/gstr"
  19. "github.com/gogf/gf/v2/util/gtag"
  20. )
  21. // OpenApiV3 is the structure defined from:
  22. // https://swagger.io/specification/
  23. // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md
  24. type OpenApiV3 struct {
  25. Config Config `json:"-"`
  26. OpenAPI string `json:"openapi"`
  27. Components Components `json:"components,omitempty"`
  28. Info Info `json:"info"`
  29. Paths Paths `json:"paths"`
  30. Security *SecurityRequirements `json:"security,omitempty"`
  31. Servers *Servers `json:"servers,omitempty"`
  32. Tags *Tags `json:"tags,omitempty"`
  33. ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
  34. }
  35. const (
  36. TypeInteger = `integer`
  37. TypeNumber = `number`
  38. TypeBoolean = `boolean`
  39. TypeArray = `array`
  40. TypeString = `string`
  41. TypeFile = `file`
  42. TypeObject = `object`
  43. FormatInt32 = `int32`
  44. FormatInt64 = `int64`
  45. FormatDouble = `double`
  46. FormatByte = `byte`
  47. FormatBinary = `binary`
  48. FormatDate = `date`
  49. FormatDateTime = `date-time`
  50. FormatPassword = `password`
  51. )
  52. const (
  53. ParameterInHeader = `header`
  54. ParameterInPath = `path`
  55. ParameterInQuery = `query`
  56. ParameterInCookie = `cookie`
  57. )
  58. const (
  59. validationRuleKeyForRequired = `required`
  60. validationRuleKeyForIn = `in:`
  61. )
  62. var (
  63. defaultReadContentTypes = []string{`application/json`}
  64. defaultWriteContentTypes = []string{`application/json`}
  65. shortTypeMapForTag = map[string]string{
  66. gtag.DefaultShort: gtag.Default,
  67. gtag.SummaryShort: gtag.Summary,
  68. gtag.SummaryShort2: gtag.Summary,
  69. gtag.DescriptionShort: gtag.Description,
  70. gtag.DescriptionShort2: gtag.Description,
  71. gtag.ExampleShort: gtag.Example,
  72. gtag.ExamplesShort: gtag.Examples,
  73. gtag.ExternalDocsShort: gtag.ExternalDocs,
  74. }
  75. )
  76. // New creates and returns an OpenApiV3 implements object.
  77. func New() *OpenApiV3 {
  78. oai := &OpenApiV3{}
  79. oai.fillWithDefaultValue()
  80. return oai
  81. }
  82. // AddInput is the structured parameter for function OpenApiV3.Add.
  83. type AddInput struct {
  84. Path string // Path specifies the custom path if this is not configured in Meta of struct tag.
  85. Prefix string // Prefix specifies the custom route path prefix, which will be added with the path tag in Meta of struct tag.
  86. Method string // Method specifies the custom HTTP method if this is not configured in Meta of struct tag.
  87. Object interface{} // Object can be an instance of struct or a route function.
  88. }
  89. // Add adds an instance of struct or a route function to OpenApiV3 definition implements.
  90. func (oai *OpenApiV3) Add(in AddInput) error {
  91. var (
  92. reflectValue = reflect.ValueOf(in.Object)
  93. )
  94. for reflectValue.Kind() == reflect.Ptr {
  95. reflectValue = reflectValue.Elem()
  96. }
  97. switch reflectValue.Kind() {
  98. case reflect.Struct:
  99. return oai.addSchema(in.Object)
  100. case reflect.Func:
  101. return oai.addPath(addPathInput{
  102. Path: in.Path,
  103. Prefix: in.Prefix,
  104. Method: in.Method,
  105. Function: in.Object,
  106. })
  107. default:
  108. return gerror.NewCodef(
  109. gcode.CodeInvalidParameter,
  110. `unsupported parameter type "%s", only struct/function type is supported`,
  111. reflect.TypeOf(in.Object).String(),
  112. )
  113. }
  114. }
  115. func (oai OpenApiV3) String() string {
  116. b, err := json.Marshal(oai)
  117. if err != nil {
  118. intlog.Errorf(context.TODO(), `%+v`, err)
  119. }
  120. return string(b)
  121. }
  122. func (oai *OpenApiV3) golangTypeToOAIType(t reflect.Type) string {
  123. for t.Kind() == reflect.Ptr {
  124. t = t.Elem()
  125. }
  126. switch t.Kind() {
  127. case reflect.String:
  128. return TypeString
  129. case reflect.Struct:
  130. switch t.String() {
  131. case `time.Time`, `gtime.Time`:
  132. return TypeString
  133. case `ghttp.UploadFile`:
  134. return TypeFile
  135. }
  136. return TypeObject
  137. case reflect.Slice, reflect.Array:
  138. switch t.String() {
  139. case `[]uint8`:
  140. return TypeString
  141. }
  142. return TypeArray
  143. case reflect.Bool:
  144. return TypeBoolean
  145. case
  146. reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
  147. reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  148. return TypeInteger
  149. case
  150. reflect.Float32, reflect.Float64,
  151. reflect.Complex64, reflect.Complex128:
  152. return TypeNumber
  153. default:
  154. return TypeObject
  155. }
  156. }
  157. // golangTypeToOAIFormat converts and returns OpenAPI parameter format for given golang type `t`.
  158. // Note that it does not return standard OpenAPI parameter format but custom format in golang type.
  159. func (oai *OpenApiV3) golangTypeToOAIFormat(t reflect.Type) string {
  160. format := t.String()
  161. switch gstr.TrimLeft(format, "*") {
  162. case `[]uint8`:
  163. return FormatBinary
  164. default:
  165. if oai.isEmbeddedStructDefinition(t) {
  166. return `EmbeddedStructDefinition`
  167. }
  168. return format
  169. }
  170. }
  171. func (oai *OpenApiV3) golangTypeToSchemaName(t reflect.Type) string {
  172. var (
  173. pkgPath string
  174. schemaName = gstr.TrimLeft(t.String(), "*")
  175. )
  176. // Pointer type has no PkgPath.
  177. for t.Kind() == reflect.Ptr {
  178. t = t.Elem()
  179. }
  180. if pkgPath = t.PkgPath(); pkgPath != "" && pkgPath != "." {
  181. if !oai.Config.IgnorePkgPath {
  182. schemaName = gstr.Replace(pkgPath, `/`, `.`) + gstr.SubStrFrom(schemaName, ".")
  183. }
  184. }
  185. schemaName = gstr.ReplaceByMap(schemaName, map[string]string{
  186. ` `: ``,
  187. `{`: ``,
  188. `}`: ``,
  189. })
  190. return schemaName
  191. }
  192. func (oai *OpenApiV3) fillMapWithShortTags(m map[string]string) map[string]string {
  193. for k, v := range shortTypeMapForTag {
  194. if m[v] == "" && m[k] != "" {
  195. m[v] = m[k]
  196. }
  197. }
  198. return m
  199. }
  200. func formatRefToBytes(ref string) []byte {
  201. return []byte(fmt.Sprintf(`{"$ref":"#/components/schemas/%s"}`, ref))
  202. }
  203. func isValidParameterName(key string) bool {
  204. return key != "-"
  205. }