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