This commit is contained in:
John Guo
2021-10-08 21:16:47 +08:00
parent b1113c328a
commit 5b2f40a454
4 changed files with 121 additions and 14 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
})
}