goai.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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. )
  20. // OpenApiV3 is the structure defined from:
  21. // https://swagger.io/specification/
  22. // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md
  23. type OpenApiV3 struct {
  24. Config Config `json:"-"`
  25. OpenAPI string `json:"openapi"`
  26. Components Components `json:"components,omitempty"`
  27. Info Info `json:"info"`
  28. Paths Paths `json:"paths"`
  29. Security *SecurityRequirements `json:"security,omitempty"`
  30. Servers *Servers `json:"servers,omitempty"`
  31. Tags *Tags `json:"tags,omitempty"`
  32. ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
  33. }
  34. const (
  35. HttpMethodGet = `GET`
  36. HttpMethodPut = `PUT`
  37. HttpMethodPost = `POST`
  38. HttpMethodDelete = `DELETE`
  39. HttpMethodConnect = `CONNECT`
  40. HttpMethodHead = `HEAD`
  41. HttpMethodOptions = `OPTIONS`
  42. HttpMethodPatch = `PATCH`
  43. HttpMethodTrace = `TRACE`
  44. )
  45. const (
  46. TypeInteger = `integer`
  47. TypeNumber = `number`
  48. TypeBoolean = `boolean`
  49. TypeArray = `array`
  50. TypeString = `string`
  51. TypeObject = `object`
  52. FormatInt32 = `int32`
  53. FormatInt64 = `int64`
  54. FormatDouble = `double`
  55. FormatByte = `byte`
  56. FormatBinary = `binary`
  57. FormatDate = `date`
  58. FormatDateTime = `date-time`
  59. FormatPassword = `password`
  60. )
  61. const (
  62. ParameterInHeader = `header`
  63. ParameterInPath = `path`
  64. ParameterInQuery = `query`
  65. ParameterInCookie = `cookie`
  66. )
  67. const (
  68. TagNamePath = `path`
  69. TagNameMethod = `method`
  70. TagNameMime = `mime`
  71. TagNameConsumes = `consumes`
  72. TagNameType = `type`
  73. TagNameDomain = `domain`
  74. )
  75. const (
  76. validationRuleKeyForRequired = `required`
  77. validationRuleKeyForIn = `in:`
  78. )
  79. var (
  80. defaultReadContentTypes = []string{`application/json`}
  81. defaultWriteContentTypes = []string{`application/json`}
  82. shortTypeMapForTag = map[string]string{
  83. "d": "Default",
  84. "sum": "Summary",
  85. "sm": "Summary",
  86. "des": "Description",
  87. "dc": "Description",
  88. "eg": "Example",
  89. "egs": "Examples",
  90. "ed": "ExternalDocs",
  91. }
  92. )
  93. // New creates and returns a OpenApiV3 implements object.
  94. func New() *OpenApiV3 {
  95. oai := &OpenApiV3{}
  96. oai.fillWithDefaultValue()
  97. return oai
  98. }
  99. // AddInput is the structured parameter for function OpenApiV3.Add.
  100. type AddInput struct {
  101. Path string // Path specifies the custom path if this is not configured in Meta of struct tag.
  102. Prefix string // Prefix specifies the custom route path prefix, which will be added with the path tag in Meta of struct tag.
  103. Method string // Method specifies the custom HTTP method if this is not configured in Meta of struct tag.
  104. Object interface{} // Object can be an instance of struct or a route function.
  105. }
  106. // Add adds an instance of struct or a route function to OpenApiV3 definition implements.
  107. func (oai *OpenApiV3) Add(in AddInput) error {
  108. var (
  109. reflectValue = reflect.ValueOf(in.Object)
  110. )
  111. for reflectValue.Kind() == reflect.Ptr {
  112. reflectValue = reflectValue.Elem()
  113. }
  114. switch reflectValue.Kind() {
  115. case reflect.Struct:
  116. return oai.addSchema(in.Object)
  117. case reflect.Func:
  118. return oai.addPath(addPathInput{
  119. Path: in.Path,
  120. Prefix: in.Prefix,
  121. Method: in.Method,
  122. Function: in.Object,
  123. })
  124. default:
  125. return gerror.NewCodef(
  126. gcode.CodeInvalidParameter,
  127. `unsupported parameter type "%s", only struct/function type is supported`,
  128. reflect.TypeOf(in.Object).String(),
  129. )
  130. }
  131. }
  132. func (oai OpenApiV3) String() string {
  133. b, err := json.Marshal(oai)
  134. if err != nil {
  135. intlog.Errorf(context.TODO(), `%+v`, err)
  136. }
  137. return string(b)
  138. }
  139. func (oai *OpenApiV3) golangTypeToOAIType(t reflect.Type) string {
  140. for t.Kind() == reflect.Ptr {
  141. t = t.Elem()
  142. }
  143. switch t.Kind() {
  144. case reflect.String:
  145. return TypeString
  146. case reflect.Struct:
  147. switch t.String() {
  148. case `time.Time`, `gtime.Time`:
  149. return TypeString
  150. }
  151. return TypeObject
  152. case reflect.Slice, reflect.Array:
  153. switch t.String() {
  154. case `[]uint8`:
  155. return TypeString
  156. }
  157. return TypeArray
  158. case reflect.Bool:
  159. return TypeBoolean
  160. case
  161. reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
  162. reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  163. return TypeInteger
  164. case
  165. reflect.Float32, reflect.Float64,
  166. reflect.Complex64, reflect.Complex128:
  167. return TypeNumber
  168. default:
  169. return TypeObject
  170. }
  171. }
  172. // golangTypeToOAIFormat converts and returns OpenAPI parameter format for given golang type `t`.
  173. // Note that it does not return standard OpenAPI parameter format but custom format in golang type.
  174. func (oai *OpenApiV3) golangTypeToOAIFormat(t reflect.Type) string {
  175. format := t.String()
  176. switch gstr.TrimLeft(format, "*") {
  177. case `[]uint8`:
  178. return FormatBinary
  179. default:
  180. if oai.isEmbeddedStructDefinition(t) {
  181. return `EmbeddedStructDefinition`
  182. }
  183. return format
  184. }
  185. }
  186. func (oai *OpenApiV3) golangTypeToSchemaName(t reflect.Type) string {
  187. var (
  188. pkgPath string
  189. schemaName = gstr.TrimLeft(t.String(), "*")
  190. )
  191. // Pointer type has no PkgPath.
  192. for t.Kind() == reflect.Ptr {
  193. t = t.Elem()
  194. }
  195. if pkgPath = t.PkgPath(); pkgPath != "" && pkgPath != "." {
  196. if !oai.Config.IgnorePkgPath {
  197. schemaName = gstr.Replace(pkgPath, `/`, `.`) + gstr.SubStrFrom(schemaName, ".")
  198. }
  199. }
  200. schemaName = gstr.ReplaceByMap(schemaName, map[string]string{
  201. ` `: ``,
  202. `{`: ``,
  203. `}`: ``,
  204. })
  205. return schemaName
  206. }
  207. func (oai *OpenApiV3) fileMapWithShortTags(m map[string]string) map[string]string {
  208. for k, v := range shortTypeMapForTag {
  209. if m[v] == "" && m[k] != "" {
  210. m[v] = m[k]
  211. }
  212. }
  213. return m
  214. }
  215. func formatRefToBytes(ref string) []byte {
  216. return []byte(fmt.Sprintf(`{"$ref":"#/components/schemas/%s"}`, ref))
  217. }
  218. func isValidParameterName(key string) bool {
  219. return key != "-"
  220. }