service.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package swagger_api
  2. import (
  3. "context"
  4. "fmt"
  5. "git.ikuban.com/server/swagger-api/v2/generator"
  6. "github.com/go-kratos/kratos/v2/api/metadata"
  7. "github.com/xmkuban/utils/utils"
  8. "google.golang.org/protobuf/compiler/protogen"
  9. "google.golang.org/protobuf/types/descriptorpb"
  10. "google.golang.org/protobuf/types/pluginpb"
  11. )
  12. // Service is service
  13. type Service struct {
  14. ser *Server
  15. }
  16. // New service
  17. func New(skipError bool) *Service {
  18. return &Service{
  19. ser: NewServer(nil, skipError),
  20. }
  21. }
  22. // ListServices list services
  23. func (s *Service) ListServices(ctx context.Context, in *metadata.ListServicesRequest) (*metadata.ListServicesReply, error) {
  24. return s.ser.ListServices(ctx, &metadata.ListServicesRequest{})
  25. }
  26. // GetServiceOpenAPI get service open api
  27. func (s *Service) GetServiceOpenAPI(ctx context.Context, in *metadata.GetServiceDescRequest) (string, error) {
  28. protoSet, err := s.ser.GetServiceDesc(ctx, in)
  29. if err != nil {
  30. return "", err
  31. }
  32. files := protoSet.FileDescSet.File
  33. newFiles := make([]*descriptorpb.FileDescriptorProto, 0)
  34. newFileMap := make(map[string]byte)
  35. for _, v := range files {
  36. if _, ok := newFileMap[v.GetName()]; !ok {
  37. newFiles = append(newFiles, v)
  38. newFileMap[v.GetName()] = 1
  39. }
  40. }
  41. files = newFiles
  42. var target string
  43. if len(files) == 0 {
  44. return "", fmt.Errorf("proto file is empty")
  45. }
  46. if files[len(files)-1].Name == nil {
  47. return "", fmt.Errorf("proto file's name is null")
  48. }
  49. target = files[len(files)-1].GetName()
  50. req := new(pluginpb.CodeGeneratorRequest)
  51. req.FileToGenerate = []string{target}
  52. var para = ""
  53. req.Parameter = &para
  54. req.ProtoFile = files
  55. opts := protogen.Options{}
  56. plugin, err := opts.New(req)
  57. if err != nil {
  58. return "", err
  59. }
  60. plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
  61. var content []byte
  62. gen := generator.NewOpenAPIv3Generator(plugin, generator.Configuration{
  63. Version: utils.ToPointString("1.0"),
  64. Title: utils.ToPointString(""),
  65. Description: utils.ToPointString(""),
  66. Naming: utils.ToPointString("proto"),
  67. FQSchemaNaming: utils.ToPointBool(true),
  68. EnumType: utils.ToPointString("integer"),
  69. CircularDepth: utils.ToPointInt(2),
  70. DefaultResponse: utils.ToPointBool(false),
  71. OutputMode: utils.ToPointString("merged"),
  72. }, plugin.Files)
  73. content, err = gen.RunV2()
  74. if err != nil {
  75. return "", err
  76. }
  77. return string(content), nil
  78. }
  79. // GetAllServicesOpenAPI get all services merged into one openapi document
  80. // servicesList: specific services to include, pass nil or empty slice to include all services
  81. // title: the title for the merged API document
  82. // description: the description for the merged API document
  83. func (s *Service) GetAllServicesOpenAPI(ctx context.Context, servicesList []string, title, description string) (string, error) {
  84. // Determine which services to process
  85. var servicesToProcess []string
  86. if len(servicesList) > 0 {
  87. // Use the specified services list
  88. servicesToProcess = servicesList
  89. } else {
  90. // Get all services if no list specified
  91. allServices, err := s.ser.ListServices(ctx, &metadata.ListServicesRequest{})
  92. if err != nil {
  93. return "", err
  94. }
  95. servicesToProcess = allServices.Services
  96. }
  97. if len(servicesToProcess) == 0 {
  98. return "", fmt.Errorf("no services found")
  99. }
  100. // Collect all proto files from specified services
  101. allFiles := make([]*descriptorpb.FileDescriptorProto, 0)
  102. fileMap := make(map[string]byte)
  103. filesToGenerate := make([]string, 0)
  104. for _, serviceName := range servicesToProcess {
  105. protoSet, err := s.ser.GetServiceDesc(ctx, &metadata.GetServiceDescRequest{Name: serviceName})
  106. if err != nil {
  107. // Skip services that fail to load
  108. continue
  109. }
  110. for _, file := range protoSet.FileDescSet.File {
  111. if _, ok := fileMap[file.GetName()]; !ok {
  112. allFiles = append(allFiles, file)
  113. fileMap[file.GetName()] = 1
  114. // Add files that contain services to the generation list
  115. if len(file.Service) > 0 {
  116. filesToGenerate = append(filesToGenerate, file.GetName())
  117. }
  118. }
  119. }
  120. }
  121. if len(allFiles) == 0 {
  122. return "", fmt.Errorf("no proto files found")
  123. }
  124. if len(filesToGenerate) == 0 {
  125. return "", fmt.Errorf("no service files found")
  126. }
  127. // Create code generator request
  128. req := new(pluginpb.CodeGeneratorRequest)
  129. req.FileToGenerate = filesToGenerate
  130. var para = ""
  131. req.Parameter = &para
  132. req.ProtoFile = allFiles
  133. opts := protogen.Options{}
  134. plugin, err := opts.New(req)
  135. if err != nil {
  136. return "", err
  137. }
  138. plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
  139. // Generate merged OpenAPI document
  140. gen := generator.NewOpenAPIv3Generator(plugin, generator.Configuration{
  141. Version: utils.ToPointString("1.0"),
  142. Title: utils.ToPointString(title),
  143. Description: utils.ToPointString(description),
  144. Naming: utils.ToPointString("proto"),
  145. FQSchemaNaming: utils.ToPointBool(true),
  146. EnumType: utils.ToPointString("integer"),
  147. CircularDepth: utils.ToPointInt(2),
  148. DefaultResponse: utils.ToPointBool(false),
  149. OutputMode: utils.ToPointString("merged"),
  150. }, plugin.Files)
  151. content, err := gen.RunV2()
  152. if err != nil {
  153. return "", err
  154. }
  155. return string(content), nil
  156. }