mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
openapi
This commit is contained in:
@ -8,8 +8,10 @@ package goai
|
||||
|
||||
// Config provides extra configuration feature for OpenApiV3 implements.
|
||||
type Config struct {
|
||||
ReadContentTypes []string // ReadContentTypes specifies the default MIME types for consuming if MIME types are not configured.
|
||||
WriteContentTypes []string // WriteContentTypes specifies the default MIME types for producing if MIME types are not configured.
|
||||
CommonResponse interface{} // Common response structure for all paths.
|
||||
CommonResponseDataField string // Common response field name to be replaced with certain business response structure.
|
||||
ReadContentTypes []string // ReadContentTypes specifies the default MIME types for consuming if MIME types are not configured.
|
||||
WriteContentTypes []string // WriteContentTypes specifies the default MIME types for producing if MIME types are not configured.
|
||||
}
|
||||
|
||||
// fillWithDefaultValue fills configuration object of `oai` with default values if these are not configured.
|
||||
|
||||
@ -87,8 +87,8 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
path = Path{}
|
||||
inputMetaMap = gmeta.Data(inputObject.Interface())
|
||||
outputMetaMap = gmeta.Data(outputObject.Interface())
|
||||
isInputStructEmpty = oai.structHasNoFields(inputObject.Interface())
|
||||
isOutputStructEmpty = oai.structHasNoFields(outputObject.Interface())
|
||||
isInputStructEmpty = oai.doesStructHasNoFields(inputObject.Interface())
|
||||
isOutputStructEmpty = oai.doesStructHasNoFields(outputObject.Interface())
|
||||
inputStructTypeName = golangTypeToSchemaName(inputObject.Type())
|
||||
outputStructTypeName = golangTypeToSchemaName(outputObject.Type())
|
||||
operation = Operation{
|
||||
@ -214,10 +214,12 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
if isOutputStructEmpty {
|
||||
response.Content[v] = MediaType{}
|
||||
} else {
|
||||
schemaRef, err := oai.getResponseSchemaRef(outputStructTypeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response.Content[v] = MediaType{
|
||||
Schema: &SchemaRef{
|
||||
Ref: outputStructTypeName,
|
||||
},
|
||||
Schema: schemaRef,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,10 +266,58 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) structHasNoFields(s interface{}) bool {
|
||||
func (oai *OpenApiV3) doesStructHasNoFields(s interface{}) bool {
|
||||
structFields, _ := structs.Fields(structs.FieldsInput{
|
||||
Pointer: s,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
return len(structFields) == 0
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) getResponseSchemaRef(responseStructName string) (*SchemaRef, error) {
|
||||
if oai.Config.CommonResponse == nil {
|
||||
return &SchemaRef{
|
||||
Ref: responseStructName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
structFields, _ := structs.Fields(structs.FieldsInput{
|
||||
Pointer: oai.Config.CommonResponse,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
var (
|
||||
err error
|
||||
bizResponseStructSchemaRef = oai.Components.Schemas[responseStructName]
|
||||
schema = &Schema{
|
||||
Properties: map[string]SchemaRef{},
|
||||
}
|
||||
)
|
||||
schema.Type = TypeObject
|
||||
for _, structField := range structFields {
|
||||
if !gstr.IsLetterUpper(structField.Name()[0]) {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
schemaRef *SchemaRef
|
||||
fieldName = structField.Name()
|
||||
)
|
||||
if jsonName := structField.TagJsonName(); jsonName != "" {
|
||||
fieldName = jsonName
|
||||
}
|
||||
if structField.Name() == oai.Config.CommonResponseDataField {
|
||||
schemaRef = &bizResponseStructSchemaRef
|
||||
} else {
|
||||
schemaRef, err = oai.newSchemaRefWithGolangType(
|
||||
structField.Type().Type,
|
||||
structField.TagMap(),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = *schemaRef
|
||||
}
|
||||
return &SchemaRef{
|
||||
Value: schema,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -86,6 +86,20 @@ func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error {
|
||||
// Take the holder first.
|
||||
oai.Components.Schemas[structTypeName] = SchemaRef{}
|
||||
|
||||
schema, err := oai.structToSchema(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oai.Components.Schemas[structTypeName] = SchemaRef{
|
||||
Ref: "",
|
||||
Value: schema,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// structToSchema converts and returns given struct object as Schema.
|
||||
func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
|
||||
structFields, _ := structs.Fields(structs.FieldsInput{
|
||||
Pointer: object,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
@ -111,13 +125,9 @@ func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error {
|
||||
structField.TagMap(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = *schemaRef
|
||||
}
|
||||
oai.Components.Schemas[structTypeName] = SchemaRef{
|
||||
Ref: "",
|
||||
Value: schema,
|
||||
}
|
||||
return nil
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
@ -188,3 +188,48 @@ func TestOpenApiV3_Add_EmptyReqAndRes(t *testing.T) {
|
||||
fmt.Println(oai.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestOpenApiV3_CommonResponse(t *testing.T) {
|
||||
type CommonResponse struct {
|
||||
Code int `json:"code" description:"Error code"`
|
||||
Message string `json:"message" description:"Error message"`
|
||||
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"GET"`
|
||||
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
|
||||
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
|
||||
}
|
||||
type Res struct {
|
||||
Product string `json:"product" v:"required" description:"Unique product key"`
|
||||
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
|
||||
Version string `json:"version" v:"required" description:"Workflow template version"`
|
||||
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
|
||||
Globals string `json:"globals" description:"Globals"`
|
||||
}
|
||||
|
||||
f := func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
err error
|
||||
oai = goai.New()
|
||||
)
|
||||
|
||||
oai.Config.CommonResponse = CommonResponse{}
|
||||
oai.Config.CommonResponseDataField = `Data`
|
||||
|
||||
err = oai.Add(goai.AddInput{
|
||||
Path: "/index",
|
||||
Object: f,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 2)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties), 3)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user