diff --git a/protoc-gen-http-swagger/generator/generator.go b/protoc-gen-http-swagger/generator/generator.go index ecc1820..51c56e3 100644 --- a/protoc-gen-http-swagger/generator/generator.go +++ b/protoc-gen-http-swagger/generator/generator.go @@ -60,10 +60,6 @@ type Configuration struct { OutputMode *string } -const ( - infoURL = "https://github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger" -) - // In order to dynamically add google.rpc.Status responses we need // to know the message descriptors for google.rpc.Status as well // as google.protobuf.Any. @@ -110,7 +106,7 @@ func (g *OpenAPIGenerator) Run(outputFile *protogen.GeneratedFile) error { func (g *OpenAPIGenerator) buildDocument() *openapi.Document { d := &openapi.Document{} - d.Openapi = "3.0.3" + d.Openapi = OpenAPIVersion d.Info = &openapi.Info{ Version: *g.conf.Version, Title: *g.conf.Title, @@ -347,7 +343,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputMessage *protogen.Message, bod } schema := &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, Properties: definitionProperties, } @@ -384,7 +380,7 @@ func (g *OpenAPIGenerator) buildOperation( // Check for each type of extension (query, path, cookie, header) if ext = proto.GetExtension(field.Desc.Options(), api.E_Query); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Query).(string) - paramIn = "query" + paramIn = ParameterInQuery paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -400,7 +396,7 @@ func (g *OpenAPIGenerator) buildOperation( } } else if ext = proto.GetExtension(field.Desc.Options(), api.E_Path); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Path).(string) - paramIn = "path" + paramIn = ParameterInPath paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -414,7 +410,7 @@ func (g *OpenAPIGenerator) buildOperation( required = true } else if ext = proto.GetExtension(field.Desc.Options(), api.E_Cookie); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Cookie).(string) - paramIn = "cookie" + paramIn = ParameterInCookie paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -426,7 +422,7 @@ func (g *OpenAPIGenerator) buildOperation( } } else if ext = proto.GetExtension(field.Desc.Options(), api.E_Header); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Header).(string) - paramIn = "header" + paramIn = ParameterInHeader paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -473,7 +469,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(bodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -486,7 +482,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(formSchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "multipart/form-data", + Name: ContentTypeFormMultipart, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -497,7 +493,7 @@ func (g *OpenAPIGenerator) buildOperation( }) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/x-www-form-urlencoded", + Name: ContentTypeFormURLEncoded, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -510,7 +506,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(rawBodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -539,7 +535,7 @@ func (g *OpenAPIGenerator) buildOperation( desc := g.filterCommentString(outputMessage.Comments.Leading) if desc == "" { - desc = "Successful response" + desc = DefaultResponseDesc } var headerOrEmpty *openapi.HeadersOrReferences @@ -626,7 +622,7 @@ func (g *OpenAPIGenerator) getResponseForMessage(d *openapi.Document, message *p ref := "#/components/schemas/" + g.reflect.formatMessageName(message.Desc) + "Body" g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Reference{ @@ -645,7 +641,7 @@ func (g *OpenAPIGenerator) getResponseForMessage(d *openapi.Document, message *p ref := "#/components/schemas/" + g.reflect.formatMessageName(message.Desc) + "RawBody" g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Reference{ @@ -660,7 +656,7 @@ func (g *OpenAPIGenerator) getResponseForMessage(d *openapi.Document, message *p AdditionalProperties: additionalProperties, } - return "200", headers, content + return StatusOK, headers, content } // addOperationToDocument adds an operation to the specified path/method. @@ -860,7 +856,7 @@ func (g *OpenAPIGenerator) addSchemasForMessagesToDocument(d *openapi.Document, } schema := &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, Description: messageDescription, Properties: definitionProperties, Required: required, @@ -883,3 +879,22 @@ func (g *OpenAPIGenerator) addSchemasForMessagesToDocument(d *openapi.Document, }) } } + +const ( + OpenAPIVersion = "3.0.3" + infoURL = "https://github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger" + + DefaultResponseDesc = "Successful response" + StatusOK = "200" + SchemaObjectType = "object" + + ContentTypeJSON = "application/json" + ContentTypeFormMultipart = "multipart/form-data" + ContentTypeFormURLEncoded = "application/x-www-form-urlencoded" + ContentTypeRawBody = "text/plain" + + ParameterInQuery = "query" + ParameterInHeader = "header" + ParameterInPath = "path" + ParameterInCookie = "cookie" +) diff --git a/protoc-gen-http-swagger/main.go b/protoc-gen-http-swagger/main.go index bd8afa0..2561d24 100644 --- a/protoc-gen-http-swagger/main.go +++ b/protoc-gen-http-swagger/main.go @@ -45,6 +45,8 @@ import ( var flags flag.FlagSet +const DefaultOutputFile = "openapi.yaml" + func main() { conf := generator.Configuration{ Version: flags.String("version", "3.0.3", "version number text, e.g. 1.2.3"), @@ -68,7 +70,7 @@ func main() { if !file.Generate { continue } - outfileName := strings.TrimSuffix(file.Desc.Path(), filepath.Ext(file.Desc.Path())) + ".openapi.yaml" + outfileName := strings.TrimSuffix(file.Desc.Path(), filepath.Ext(file.Desc.Path())) + "." + DefaultOutputFile outputFile := plugin.NewGeneratedFile(outfileName, "") gen := generator.NewOpenAPIGenerator(plugin, conf, []*protogen.File{file}) if err := gen.Run(outputFile); err != nil { @@ -76,7 +78,7 @@ func main() { } } } else { - outputFile := plugin.NewGeneratedFile("openapi.yaml", "") + outputFile := plugin.NewGeneratedFile(DefaultOutputFile, "") return generator.NewOpenAPIGenerator(plugin, conf, plugin.Files).Run(outputFile) } return nil diff --git a/thrift-gen-http-swagger/generator/generator.go b/thrift-gen-http-swagger/generator/generator.go index 67a934a..f07e907 100644 --- a/thrift-gen-http-swagger/generator/generator.go +++ b/thrift-gen-http-swagger/generator/generator.go @@ -49,10 +49,6 @@ import ( "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/utils" ) -const ( - infoURL = "https://github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger" -) - type OpenAPIGenerator struct { fileDesc *thrift_reflection.FileDescriptor ast *parser.Thrift @@ -77,12 +73,12 @@ func NewOpenAPIGenerator(ast *parser.Thrift) *OpenAPIGenerator { func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Generated { d := &openapi.Document{} - version := "3.0.3" + version := OpenAPIVersion d.Openapi = version d.Info = &openapi.Info{ - Title: "API generated by thrift-gen-http-swagger", - Description: "API description", - Version: "1.0.0", + Title: DefaultInfoTitle, + Description: DefaultInfoDesc, + Version: DefaultInfoVersion, } d.Paths = &openapi.Paths{} d.Components = &openapi.Components{ @@ -212,7 +208,7 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge return nil } filePath := filepath.Clean(arguments.OutputDir) - filePath = filepath.Join(filePath, "openapi.yaml") + filePath = filepath.Join(filePath, DefaultOutputFile) var ret []*plugin.Generated ret = append(ret, &plugin.Generated{ Content: string(bytes), @@ -224,13 +220,13 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge func (g *OpenAPIGenerator) getDocumentOption(obj interface{}) error { serviceOrStruct, name := g.getDocumentAnnotationInWhichServiceOrStruct() - if serviceOrStruct == "service" { + if serviceOrStruct == DocumentOptionServiceType { serviceDesc := g.fileDesc.GetServiceDescriptor(name) err := utils.ParseServiceOption(serviceDesc, OpenapiDocument, obj) if err != nil { return err } - } else if serviceOrStruct == "struct" { + } else if serviceOrStruct == DocumentOptionStructType { structDesc := g.fileDesc.GetStructDescriptor(name) err := utils.ParseStructOption(structDesc, OpenapiDocument, obj) if err != nil { @@ -317,7 +313,7 @@ func (g *OpenAPIGenerator) buildOperation( extOrNil := v.Annotations[ApiQuery] if len(extOrNil) > 0 { if ext := v.Annotations[ApiQuery][0]; ext != "" { - paramIn = "query" + paramIn = ParameterInQuery paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) @@ -335,7 +331,7 @@ func (g *OpenAPIGenerator) buildOperation( extOrNil = v.Annotations[ApiPath] if len(extOrNil) > 0 { if ext := v.Annotations[ApiPath][0]; ext != "" { - paramIn = "path" + paramIn = ParameterInPath paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) @@ -354,7 +350,7 @@ func (g *OpenAPIGenerator) buildOperation( extOrNil = v.Annotations[ApiCookie] if len(extOrNil) > 0 { if ext := v.Annotations[ApiCookie][0]; ext != "" { - paramIn = "cookie" + paramIn = ParameterInCookie paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) @@ -372,7 +368,7 @@ func (g *OpenAPIGenerator) buildOperation( extOrNil = v.Annotations[ApiHeader] if len(extOrNil) > 0 { if ext := v.Annotations[ApiHeader][0]; ext != "" { - paramIn = "header" + paramIn = ParameterInHeader paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) @@ -420,7 +416,7 @@ func (g *OpenAPIGenerator) buildOperation( var additionalProperties []*openapi.NamedMediaType if len(bodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Schema: bodySchema, @@ -431,7 +427,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(formSchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "multipart/form-data", + Name: ContentTypeFormMultipart, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Schema: formSchema, @@ -440,7 +436,7 @@ func (g *OpenAPIGenerator) buildOperation( }) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/x-www-form-urlencoded", + Name: ContentTypeFormURLEncoded, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Schema: formSchema, @@ -451,7 +447,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(rawBodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Schema: rawBodySchema, @@ -477,7 +473,7 @@ func (g *OpenAPIGenerator) buildOperation( desc := g.filterCommentString(outputDesc.Comments) if desc == "" { - desc = "Successful response" + desc = DefaultResponseDesc } var headerOrEmpty *openapi.HeadersOrReferences @@ -538,14 +534,14 @@ func (g *OpenAPIGenerator) getDocumentAnnotationInWhichServiceOrStruct() (string v := s.Annotations.Get(OpenapiDocument) if len(v) > 0 { ret = s.GetName() - return "service", ret + return DocumentOptionServiceType, ret } } for _, s := range g.ast.Structs { v := s.Annotations.Get(OpenapiDocument) if len(v) > 0 { ret = s.GetName() - return "struct", ret + return DocumentOptionStructType, ret } } return "", ret @@ -586,7 +582,7 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif ref := "#/components/schemas/" + desc.GetName() + "Body" g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, @@ -603,7 +599,7 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif ref := "#/components/schemas/" + desc.GetName() + "RawBody" g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, @@ -616,7 +612,7 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif AdditionalProperties: additionalProperties, } - return "200", headers, content + return StatusOK, headers, content } func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.StructDescriptor, option string) *openapi.Schema { @@ -676,7 +672,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.Struct } schema := &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, Properties: definitionProperties, } @@ -803,7 +799,7 @@ func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, s } schema := &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, Description: messageDescription, Properties: definitionProperties, } @@ -878,121 +874,72 @@ func (g *OpenAPIGenerator) schemaReferenceForMessage(message *thrift_reflection. func (g *OpenAPIGenerator) schemaOrReferenceForField(fieldType *thrift_reflection.TypeDescriptor) *openapi.SchemaOrReference { var kindSchema *openapi.SchemaOrReference - if fieldType.IsStruct() { + + switch { + case fieldType.IsStruct(): structDesc, err := fieldType.GetStructDescriptor() if err != nil { logs.Errorf("Error getting struct descriptor: %s", err) return nil } ref := g.schemaReferenceForMessage(structDesc) - kindSchema = &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, } - } - - if fieldType.GetName() == "string" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - }, - } - } - - if fieldType.GetName() == "binary" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - Format: "binary", - }, - } - } - if fieldType.GetName() == "bool" { + case fieldType.IsMap(): + valueSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) kindSchema = &openapi.SchemaOrReference{ Schema: &openapi.Schema{ - Type: "boolean", - }, - } - } - - if fieldType.GetName() == "byte" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - Format: "byte", - }, - } - } - - if fieldType.GetName() == "double" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "number", - Format: "double", - }, - } - } - - if fieldType.GetName() == "i8" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int8", - }, - } - } - - if fieldType.GetName() == "i16" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int16", - }, - } - } - - if fieldType.GetName() == "i32" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int32", - }, - } - } - - if fieldType.GetName() == "i64" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int64", - }, - } - } - - if fieldType.IsMap() { - kindSchema = g.schemaOrReferenceForField(fieldType.GetValueType()) - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, AdditionalProperties: &openapi.AdditionalPropertiesItem{ - SchemaOrReference: kindSchema, + SchemaOrReference: valueSchema, }, }, } - } - if fieldType.IsList() { - kindSchema = g.schemaOrReferenceForField(fieldType.GetValueType()) + case fieldType.IsList(): + itemSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) kindSchema = &openapi.SchemaOrReference{ Schema: &openapi.Schema{ Type: "array", Items: &openapi.ItemsItem{ - SchemaOrReference: []*openapi.SchemaOrReference{kindSchema}, + SchemaOrReference: []*openapi.SchemaOrReference{itemSchema}, }, }, } + + default: + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + switch fieldType.GetName() { + case "string": + kindSchema.Schema.Type = "string" + case "binary": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "binary" + case "bool": + kindSchema.Schema.Type = "boolean" + case "byte": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "byte" + case "double": + kindSchema.Schema.Type = "number" + kindSchema.Schema.Format = "double" + case "i8": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int8" + case "i16": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int16" + case "i32": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int32" + case "i64": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int64" + } } + return kindSchema } @@ -1031,3 +978,29 @@ var HttpMethodAnnotations = map[string]string{ ApiHEAD: "HEAD", ApiAny: "ANY", } + +const ( + OpenAPIVersion = "3.0.3" + DefaultOutputFile = "openapi.yaml" + DefaultInfoTitle = "API generated by thrift-gen-http-swagger" + DefaultInfoDesc = "API description" + DefaultInfoVersion = "0.0.1" + infoURL = "https://github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger" + + DefaultResponseDesc = "Successful response" + StatusOK = "200" + SchemaObjectType = "object" + + ContentTypeJSON = "application/json" + ContentTypeFormMultipart = "multipart/form-data" + ContentTypeFormURLEncoded = "application/x-www-form-urlencoded" + ContentTypeRawBody = "text/plain" + + ParameterInQuery = "query" + ParameterInHeader = "header" + ParameterInPath = "path" + ParameterInCookie = "cookie" + + DocumentOptionServiceType = "service" + DocumentOptionStructType = "struct" +) diff --git a/thrift-gen-http-swagger/plugins/plugins.go b/thrift-gen-http-swagger/plugins/plugins.go index d3aa971..b40e3c9 100644 --- a/thrift-gen-http-swagger/plugins/plugins.go +++ b/thrift-gen-http-swagger/plugins/plugins.go @@ -50,15 +50,16 @@ func Run() int { } func handleRequest(req *plugin.Request) (err error) { + if req == nil { + fmt.Fprintf(os.Stderr, "unexpected nil request") + } + args := new(args.Arguments) if err := args.Unpack(req.PluginParameters); err != nil { log.Printf("[Error]: unpack args failed: %s", err.Error()) return err } - if req == nil { - fmt.Fprintf(os.Stderr, "unexpected nil request") - } ast := req.GetAST() g := generator.NewOpenAPIGenerator(ast) contents := g.BuildDocument(args) diff --git a/thrift-gen-rpc-swagger/example/docs/openapi.yaml b/thrift-gen-rpc-swagger/example/docs/openapi.yaml index 01a3b3a..530d88a 100644 --- a/thrift-gen-rpc-swagger/example/docs/openapi.yaml +++ b/thrift-gen-rpc-swagger/example/docs/openapi.yaml @@ -17,8 +17,9 @@ paths: parameters: - name: ttheader in: query + description: metainfo for request schema: - type: object + type: object requestBody: description: BodyReq content: @@ -47,8 +48,9 @@ paths: parameters: - name: ttheader in: query + description: metainfo for request schema: - type: object + type: object requestBody: description: PathReq content: @@ -74,8 +76,9 @@ paths: parameters: - name: ttheader in: query + description: metainfo for request schema: - type: object + type: object requestBody: description: QueryReq content: @@ -89,7 +92,7 @@ paths: minLength: 1 type: string description: Name - items: + Items: type: array items: type: string @@ -104,6 +107,8 @@ components: schemas: HelloResp: title: Hello - response + required: + - RespBody type: object properties: RespBody: @@ -119,3 +124,4 @@ components: description: Hello - response tags: - name: HelloService1 + description: HelloService1描述 diff --git a/thrift-gen-rpc-swagger/example/hello.thrift b/thrift-gen-rpc-swagger/example/hello.thrift index bf74da0..59bdc37 100644 --- a/thrift-gen-rpc-swagger/example/hello.thrift +++ b/thrift-gen-rpc-swagger/example/hello.thrift @@ -5,10 +5,6 @@ include "openapi.thrift" // QueryReq struct QueryReq { 1: string QueryValue ( - api.query = "query2", - openapi.parameter = '{ - required: true - }', openapi.property = '{ title: "Name", description: "Name", @@ -17,35 +13,27 @@ struct QueryReq { max_length: 50 }' ) - 2: list items ( - api.query = "items" - ) + 2: list Items () } // PathReq struct PathReq { //field: path描述 - 1: string PathValue ( - api.path = "path1" - ) + 1: string PathValue () } //BodyReq struct BodyReq { //field: body描述 - 1: string BodyValue ( - api.body = "body" - ) + 1: string BodyValue () + //field: query描述 - 2: string QueryValue ( - api.query = "query2" - ) + 2: string QueryValue () } // HelloResp struct HelloResp { 1: string RespBody ( - api.body = "body", openapi.property = '{ title: "response content", description: "response content", @@ -55,7 +43,6 @@ struct HelloResp { }' ) 2: string token ( - api.header = "token", openapi.property = '{ title: "token", description: "token", @@ -67,24 +54,18 @@ struct HelloResp { title: "Hello - response", description: "Hello - response", required: [ - "body" + "RespBody" ] }' ) // HelloService1描述 service HelloService1 { - HelloResp QueryMethod(1: QueryReq req) ( - api.get = "/hello1" - ) + HelloResp QueryMethod(1: QueryReq req) () - HelloResp PathMethod(1: PathReq req) ( - api.get = "/path:path1" - ) + HelloResp PathMethod(1: PathReq req) () - HelloResp BodyMethod(1: BodyReq req) ( - api.post = "/body" - ) + HelloResp BodyMethod(1: BodyReq req) () }( api.base_domain = "127.0.0.1:8080", openapi.document = '{ diff --git a/thrift-gen-rpc-swagger/generator/openapi_gen.go b/thrift-gen-rpc-swagger/generator/openapi_gen.go index fb3c46c..f2d581a 100644 --- a/thrift-gen-rpc-swagger/generator/openapi_gen.go +++ b/thrift-gen-rpc-swagger/generator/openapi_gen.go @@ -49,10 +49,6 @@ import ( "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/utils" ) -const ( - infoURL = "https://github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger" -) - type OpenAPIGenerator struct { fileDesc *thrift_reflection.FileDescriptor ast *parser.Thrift @@ -77,12 +73,12 @@ func NewOpenAPIGenerator(ast *parser.Thrift) *OpenAPIGenerator { func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Generated { d := &openapi.Document{} - version := "3.0.3" + version := OpenAPIVersion d.Openapi = version d.Info = &openapi.Info{ - Title: "API generated by thrift-gen-rpc-swagger", - Description: "API description", - Version: "1.0.0", + Title: DefaultInfoTitle, + Description: DefaultInfoDesc, + Version: DefaultInfoVersion, } d.Paths = &openapi.Paths{} d.Components = &openapi.Components{ @@ -122,7 +118,6 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge if d.Info.Description == "" { d.Info.Description = d.Tags[0].Description } - d.Tags[0].Description = "" } var allServers []string @@ -131,45 +126,17 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge for _, path := range d.Paths.Path { var servers []string // Only 1 server will ever be set, per method, by the generator - if path.Value.Get != nil && len(path.Value.Get.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Get.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Get.Servers[0].URL) - } if path.Value.Post != nil && len(path.Value.Post.Servers) == 1 { servers = utils.AppendUnique(servers, path.Value.Post.Servers[0].URL) allServers = utils.AppendUnique(allServers, path.Value.Post.Servers[0].URL) } - if path.Value.Put != nil && len(path.Value.Put.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Put.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Put.Servers[0].URL) - } - if path.Value.Delete != nil && len(path.Value.Delete.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Delete.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Delete.Servers[0].URL) - } - if path.Value.Patch != nil && len(path.Value.Patch.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Patch.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Patch.Servers[0].URL) - } if len(servers) == 1 { path.Value.Servers = []*openapi.Server{{URL: servers[0]}} - if path.Value.Get != nil { - path.Value.Get.Servers = nil - } if path.Value.Post != nil { path.Value.Post.Servers = nil } - if path.Value.Put != nil { - path.Value.Put.Servers = nil - } - if path.Value.Delete != nil { - path.Value.Delete.Servers = nil - } - if path.Value.Patch != nil { - path.Value.Patch.Servers = nil - } } } @@ -191,7 +158,7 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge // If there are no servers, add a default one if len(allServers) == 0 { d.Servers = []*openapi.Server{ - {URL: "http://127.0.0.1:8080"}, + {URL: DefaultServerURL}, } } @@ -225,7 +192,7 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge return nil } filePath := filepath.Clean(arguments.OutputDir) - filePath = filepath.Join(filePath, "openapi.yaml") + filePath = filepath.Join(filePath, DefaultOutputFile) var ret []*plugin.Generated ret = append(ret, &plugin.Generated{ Content: string(bytes), @@ -237,13 +204,13 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge func (g *OpenAPIGenerator) getDocumentOption(obj interface{}) error { serviceOrStruct, name := g.getDocumentAnnotationInWhichServiceOrStruct() - if serviceOrStruct == "service" { + if serviceOrStruct == DocumentOptionServiceType { serviceDesc := g.fileDesc.GetServiceDescriptor(name) err := utils.ParseServiceOption(serviceDesc, OpenapiDocument, obj) if err != nil { return err } - } else if serviceOrStruct == "struct" { + } else if serviceOrStruct == DocumentOptionStructType { structDesc := g.fileDesc.GetStructDescriptor(name) err := utils.ParseStructOption(structDesc, OpenapiDocument, obj) if err != nil { @@ -319,13 +286,13 @@ func (g *OpenAPIGenerator) buildOperation( fieldSchema := &openapi.SchemaOrReference{ Schema: &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, }, } parameter := &openapi.Parameter{ - Name: "ttheader", - In: "query", - Description: "", + Name: ParameterNameTTHeader, + In: ParameterInQuery, + Description: ParameterDescription, Required: false, Schema: fieldSchema, } @@ -339,7 +306,7 @@ func (g *OpenAPIGenerator) buildOperation( var additionalProperties []*openapi.NamedMediaType if len(bodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Schema: bodySchema, @@ -363,7 +330,7 @@ func (g *OpenAPIGenerator) buildOperation( desc := g.filterCommentString(outputDesc.Comments) if desc == "" { - desc = "Successful response" + desc = DefaultResponseDesc } var headerOrEmpty *openapi.HeadersOrReferences @@ -424,14 +391,14 @@ func (g *OpenAPIGenerator) getDocumentAnnotationInWhichServiceOrStruct() (string v := s.Annotations.Get(OpenapiDocument) if len(v) > 0 { ret = s.GetName() - return "service", ret + return DocumentOptionServiceType, ret } } for _, s := range g.ast.Structs { v := s.Annotations.Get(OpenapiDocument) if len(v) > 0 { ret = s.GetName() - return "struct", ret + return DocumentOptionStructType, ret } } return "", ret @@ -453,7 +420,7 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif ref := "#/components/schemas/" + desc.GetName() g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, @@ -466,7 +433,7 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif AdditionalProperties: additionalProperties, } - return "200", headers, content + return StatusOK, headers, content } func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.StructDescriptor) *openapi.Schema { @@ -524,7 +491,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.Struct } schema := &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, Properties: definitionProperties, } @@ -634,7 +601,7 @@ func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, s } schema := &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, Description: messageDescription, Properties: definitionProperties, } @@ -697,121 +664,72 @@ func (g *OpenAPIGenerator) schemaReferenceForMessage(message *thrift_reflection. func (g *OpenAPIGenerator) schemaOrReferenceForField(fieldType *thrift_reflection.TypeDescriptor) *openapi.SchemaOrReference { var kindSchema *openapi.SchemaOrReference - if fieldType.IsStruct() { + + switch { + case fieldType.IsStruct(): structDesc, err := fieldType.GetStructDescriptor() if err != nil { logs.Errorf("Error getting struct descriptor: %s", err) return nil } ref := g.schemaReferenceForMessage(structDesc) - kindSchema = &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, } - } - - if fieldType.GetName() == "string" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - }, - } - } - - if fieldType.GetName() == "binary" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - Format: "binary", - }, - } - } - - if fieldType.GetName() == "bool" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "boolean", - }, - } - } - - if fieldType.GetName() == "byte" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - Format: "byte", - }, - } - } - if fieldType.GetName() == "double" { + case fieldType.IsMap(): + valueSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) kindSchema = &openapi.SchemaOrReference{ Schema: &openapi.Schema{ - Type: "number", - Format: "double", - }, - } - } - - if fieldType.GetName() == "i8" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int8", - }, - } - } - - if fieldType.GetName() == "i16" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int16", - }, - } - } - - if fieldType.GetName() == "i32" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int32", - }, - } - } - - if fieldType.GetName() == "i64" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int64", - }, - } - } - - if fieldType.IsMap() { - kindSchema = g.schemaOrReferenceForField(fieldType.GetValueType()) - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "object", + Type: SchemaObjectType, AdditionalProperties: &openapi.AdditionalPropertiesItem{ - SchemaOrReference: kindSchema, + SchemaOrReference: valueSchema, }, }, } - } - if fieldType.IsList() { - kindSchema = g.schemaOrReferenceForField(fieldType.GetValueType()) + case fieldType.IsList(): + itemSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) kindSchema = &openapi.SchemaOrReference{ Schema: &openapi.Schema{ Type: "array", Items: &openapi.ItemsItem{ - SchemaOrReference: []*openapi.SchemaOrReference{kindSchema}, + SchemaOrReference: []*openapi.SchemaOrReference{itemSchema}, }, }, } + + default: + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + switch fieldType.GetName() { + case "string": + kindSchema.Schema.Type = "string" + case "binary": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "binary" + case "bool": + kindSchema.Schema.Type = "boolean" + case "byte": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "byte" + case "double": + kindSchema.Schema.Type = "number" + kindSchema.Schema.Format = "double" + case "i8": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int8" + case "i16": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int16" + case "i32": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int32" + case "i64": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int64" + } } + return kindSchema } @@ -823,3 +741,26 @@ const ( OpenapiSchema = "openapi.schema" OpenapiDocument = "openapi.document" ) + +const ( + OpenAPIVersion = "3.0.3" + DefaultOutputFile = "openapi.yaml" + DefaultServerURL = "http://127.0.0.1:8080" + DefaultInfoTitle = "API generated by thrift-gen-rpc-swagger" + DefaultInfoDesc = "API description" + DefaultInfoVersion = "0.0.1" + infoURL = "https://github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger" + + DefaultResponseDesc = "Successful response" + StatusOK = "200" + + ContentTypeJSON = "application/json" + SchemaObjectType = "object" + + ParameterInQuery = "query" + ParameterNameTTHeader = "ttheader" + ParameterDescription = "metainfo for request" + + DocumentOptionServiceType = "service" + DocumentOptionStructType = "struct" +) diff --git a/thrift-gen-rpc-swagger/plugins/plugins.go b/thrift-gen-rpc-swagger/plugins/plugins.go index 862f71a..a0e97b1 100644 --- a/thrift-gen-rpc-swagger/plugins/plugins.go +++ b/thrift-gen-rpc-swagger/plugins/plugins.go @@ -50,16 +50,16 @@ func Run() int { } func handleRequest(req *plugin.Request) (err error) { + if req == nil { + fmt.Fprintf(os.Stderr, "unexpected nil request") + } + args := new(args.Arguments) if err := args.Unpack(req.PluginParameters); err != nil { log.Printf("[Error]: unpack args failed: %s", err.Error()) return err } - if req == nil { - fmt.Fprintf(os.Stderr, "unexpected nil request") - } - ast := req.GetAST() og := generator.NewOpenAPIGenerator(ast)