github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/courier/swagger/gen/openapi_generator.go (about)

     1  package gen
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"go/parser"
     7  	"go/types"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"github.com/go-courier/oas"
    12  	"golang.org/x/tools/go/loader"
    13  
    14  	"github.com/artisanhe/tools/codegen"
    15  )
    16  
    17  type SwaggerGenerator struct {
    18  	RootRouterName  string
    19  	Next            bool
    20  	pkgImportPath   string
    21  	program         *loader.Program
    22  	openapi         *oas.OpenAPI
    23  	routerScanner   *RouterScanner
    24  	operatorScanner *OperatorScanner
    25  }
    26  
    27  func (g *SwaggerGenerator) Load(cwd string) {
    28  	ldr := loader.Config{
    29  		ParserMode: parser.ParseComments,
    30  	}
    31  
    32  	pkgImportPath := codegen.GetPackageImportPath(cwd)
    33  	ldr.Import(pkgImportPath)
    34  
    35  	p, err := ldr.Load()
    36  	if err != nil {
    37  		panic(err)
    38  	}
    39  
    40  	g.program = p
    41  	g.pkgImportPath = pkgImportPath
    42  	g.openapi = oas.NewOpenAPI()
    43  	g.operatorScanner = NewOperatorScanner(p)
    44  	g.routerScanner = NewRouterScanner(p)
    45  }
    46  
    47  func (g *SwaggerGenerator) Pick() {
    48  	for _, pkgInfo := range g.program.AllPackages {
    49  		for _, def := range pkgInfo.Defs {
    50  			if typeVar, ok := def.(*types.Var); ok {
    51  				if typeVar.Name() == g.RootRouterName {
    52  					router := g.routerScanner.Router(typeVar)
    53  					routes := router.Routes(g.program)
    54  					operationIDs := map[string]*Route{}
    55  					for _, route := range routes {
    56  						operation := g.getOperationByOperatorTypes(route.Method, route.operators...)
    57  						if _, exists := operationIDs[operation.OperationId]; exists {
    58  							panic(fmt.Errorf("operationID %s should be unique", operation.OperationId))
    59  						}
    60  						operationIDs[operation.OperationId] = route
    61  						g.openapi.AddOperation(oas.HttpMethod(strings.ToLower(route.Method)), g.patchPath(route.Path, operation), operation)
    62  					}
    63  					g.operatorScanner.BindSchemas(g.openapi)
    64  					return
    65  				}
    66  			}
    67  		}
    68  	}
    69  }
    70  
    71  var RxHttpRouterPath = regexp.MustCompile("/:([^/]+)")
    72  
    73  func (g *SwaggerGenerator) patchPath(swaggerPath string, operation *oas.Operation) string {
    74  	return RxHttpRouterPath.ReplaceAllStringFunc(swaggerPath, func(str string) string {
    75  		name := RxHttpRouterPath.FindAllStringSubmatch(str, -1)[0][1]
    76  
    77  		var isParameterDefined = false
    78  
    79  		for _, parameter := range operation.Parameters {
    80  			if parameter.In == "path" && parameter.Name == name {
    81  				isParameterDefined = true
    82  			}
    83  		}
    84  
    85  		if isParameterDefined {
    86  			return "/{" + name + "}"
    87  		}
    88  
    89  		return "/0"
    90  	})
    91  }
    92  
    93  func (g *SwaggerGenerator) getOperationByOperatorTypes(method string, operatorTypes ...*OperatorTypeName) *oas.Operation {
    94  	operators := make([]Operator, 0)
    95  
    96  	for _, operatorType := range operatorTypes {
    97  		operators = append(operators, *g.operatorScanner.Operator(operatorType.TypeName))
    98  	}
    99  
   100  	return ConcatToOperation(method, operators...)
   101  }
   102  
   103  func (g *SwaggerGenerator) Output(cwd string) codegen.Outputs {
   104  	bytes, _ := json.MarshalIndent(g.openapi, "", "  ")
   105  
   106  	return codegen.Outputs{
   107  		"swagger.json": string(bytes),
   108  	}
   109  }