github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/courier/swagger/gen/openapi_generator.go (about)

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