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  }