Skip to content

Commit

Permalink
Add helper to restore one-line readability for routes
Browse files Browse the repository at this point in the history
  • Loading branch information
daemonfire300 committed Apr 27, 2021
1 parent ba58ac2 commit d159c80
Show file tree
Hide file tree
Showing 6 changed files with 546 additions and 484 deletions.
106 changes: 74 additions & 32 deletions http/jsonapi/generator/generate_handler.go
Expand Up @@ -75,6 +75,7 @@ func (g *Generator) BuildHandler(schema *openapi3.Swagger) error {
funcs := []routeGeneratorFunc{
g.generateRequestResponseTypes,
g.buildServiceInterface,
g.buildRouterHelpers,
g.buildRouter,
g.buildRouterWithFallbackAsArg,
}
Expand Down Expand Up @@ -382,11 +383,63 @@ func (g *Generator) buildRouterWithFallbackAsArg(routes []*route, schema *openap

} else {
g.goSource.Func().Id("RouterWithFallback").Params(
serviceInterfaceVariable).Op("*").Qual(pkgGorillaMux, "Router").Block(routerBody...)
serviceInterfaceVariable, jen.Id("fallback").Qual("net/http", "Handler")).Op("*").Qual(pkgGorillaMux, "Router").Block(routerBody...)
}
return nil
}

func (g *Generator) buildRouterHelpers(routes []*route, schema *openapi3.Swagger) error {
needsSecurity := hasSecuritySchema(schema)

// sort the routes with query parameter to the top
sortableRoutes := sortableRouteList(routes)
sort.Stable(&sortableRoutes)

fallbackName := "fallback"
fallback := jen.Id(fallbackName).Qual("net/http", "Handler")
// add all route handlers
for i := 0; i < len(sortableRoutes); i++ {
route := sortableRoutes[i]
var routeCallParams *jen.Statement
if needsSecurity {
routeCallParams = jen.List(jen.Id("service"), jen.Id("authBackend"))
} else {
routeCallParams = jen.List(jen.Id("service"))
}
primaryHandler := jen.Id(route.handler).Call(routeCallParams)
fallbackHandler := jen.Id(fallbackName)
ifElse := make([]jen.Code, 0)
for _, handler := range []jen.Code{primaryHandler, fallbackHandler} {
block := jen.Return(handler)
ifElse = append(ifElse, block)
}

if len(ifElse) < 1 {
panic("if-else slice should contain two elements, one with the service interface being called and one passing the NotFoundHandler")
}

implGuard := jen.If(
jen.List(jen.Id("service"), jen.Id("ok")).Op(":=").Id("service").Assert(jen.Id(generateSubServiceName(route.handler))),
jen.Id("ok")).Block(ifElse[0]).Else().Block(ifElse[1])

comment := jen.Commentf("%s helper that checks if the given service fulfills the interface. Returns fallback handler if not, otherwise returns matching handler.", generateHandlerTypeAssertionHelperName(route.handler))

var callParams *jen.Statement
if needsSecurity {
callParams = jen.List(jen.Id("service").Id("interface{}"), fallback, jen.Id("authBackend").Id(authBackendInterface))
} else {
callParams = jen.List(jen.Id("service").Id("interface{}"), fallback)
}
helper := jen.Func().Id(generateHandlerTypeAssertionHelperName(route.handler)).
Params(callParams).Qual("net/http", "Handler").Block(implGuard).Line().Line()

g.goSource.Line().Add(comment)
g.goSource.Add(helper)
}

return nil
}

func (g *Generator) buildRouterBodyWithFallback(routes []*route, schema *openapi3.Swagger, fallback jen.Code) ([]jen.Code, error) {
needsSecurity := hasSecuritySchema(schema)
startInd := 0
Expand Down Expand Up @@ -442,45 +495,30 @@ func (g *Generator) buildRouterBodyWithFallback(routes []*route, schema *openapi
route := sortableRoutes[i]
var routeCallParams *jen.Statement
if needsSecurity {
routeCallParams = jen.List(jen.Id("service"), jen.Id("authBackend"))
routeCallParams = jen.List(jen.Id("service"), fallback, jen.Id("authBackend"))
} else {
routeCallParams = jen.List(jen.Id("service"))
routeCallParams = jen.List(jen.Id("service"), fallback)
}
primaryHandler := jen.Id(route.handler).Call(routeCallParams)
fallbackHandler := fallback
ifElse := make([]jen.Code, 0)
for _, handler := range []jen.Code{primaryHandler, fallbackHandler} {
// build single route
routeStmt := jen.Id(subrouterID).Dot("Methods").Call(jen.Lit(route.method)).
Dot("Path").Call(jen.Lit(route.url.Path)).
Dot("Handler").Call(handler)

// add query parameters for route matching
if len(route.queryValues) > 0 {
for key, value := range route.queryValues {
if len(value) != 1 {
panic("query paths can only handle one query parameter with the same name!")
}
routeStmt.Dot("Queries").Call(jen.Lit(key), jen.Lit(value[0]))
helper := jen.Id(generateHandlerTypeAssertionHelperName(route.handler)).Call(routeCallParams)
routeStmt := jen.Id(subrouterID).Dot("Methods").Call(jen.Lit(route.method)).
Dot("Path").Call(jen.Lit(route.url.Path))

// add query parameters for route matching
if len(route.queryValues) > 0 {
for key, value := range route.queryValues {
if len(value) != 1 {
panic("query paths can only handle one query parameter with the same name!")
}
routeStmt.Dot("Queries").Call(jen.Lit(key), jen.Lit(value[0]))
}

// add the name to build routes
routeStmt.Dot("Name").Call(jen.Lit(route.serviceFunc))

// add to control-flow
ifElse = append(ifElse, routeStmt)
}

if len(ifElse) < 1 {
panic("if-else slice should contain two elements, one with the service interface being called and one passing the NotFoundHandler")
}
// add the name to build routes
routeStmt.Dot("Name").Call(jen.Lit(route.serviceFunc))

implGuard := jen.If(
jen.List(jen.Id("service"), jen.Id("ok")).Op(":=").Id("service").Assert(jen.Id(generateSubServiceName(route.handler))),
jen.Id("ok")).Block(ifElse[0]).Else().Block(ifElse[1])
routeStmt.Dot("Handler").Call(helper)

routeStmts = append(routeStmts, implGuard)
routeStmts = append(routeStmts, routeStmt)

}
}
Expand Down Expand Up @@ -821,3 +859,7 @@ func generateParamName(param *openapi3.ParameterRef) string {
func generateSubServiceName(handler string) string {
return fmt.Sprintf("%s%s", handler, serviceInterface)
}

func generateHandlerTypeAssertionHelperName(handler string) string {
return fmt.Sprintf("%sWithFallbackHelper", handler)
}
65 changes: 34 additions & 31 deletions http/jsonapi/generator/internal/articles/open-api_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d159c80

Please sign in to comment.