// 2017 Rackspace, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // GinHelper: This package will help in creating consistent API routes across our apps. With the GinHelper, we // will be able to define a set of Gin route groups, then attach the StartGin method to kick off the http.Server // Example would be: // Within main() // func main() { // gin.SetMode(gin.ReleaseMode) // maintpage.Run() // } package rgin import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.rackspace.com/gorax/tools/raxmiddleware" ) type ( // client type to be used as a method receiver in the Gin helper client struct { port string staging bool middleware []gin.HandlerFunc groups []GinRouteGroup statics map[string]string } // GinRouteGroup type represents a Gin Group with the base path and // RBAC role assigned. GinRouteGroup struct { BasePath string Middleware []gin.HandlerFunc Methods []GinRoute } // GinRoute type is a sub-route used to define parameters needed for the Gin Handler func GinRoute struct { Method string RelativePath string Handlers gin.HandlerFunc } ) // NewGinRoutes will create a new private client that is used to defines the routes and middleware // used in the NSI API Apps. func NewGinRoutes(r []GinRouteGroup, staging bool) *client { level := logrus.InfoLevel if gin.IsDebugging() { level = logrus.DebugLevel } return &client{ groups: r, staging: staging, statics: make(map[string]string), middleware: []gin.HandlerFunc{ raxmiddleware.Boiler(), // Boiler for per-request API calls raxmiddleware.Lagger(level), // Log client to log to Splunk gin.Recovery(), // Recover from panics and write a 500 response cors.Default(), // Enable Cross-Origin Resource Sharing }, } } // SetAddress method will define the listening address that the Gin Engine will be launched on. // Default is ":8080" func (n *client) SetAddress(address string) { n.port = address } // Static method is used to append static served files onto the existing routes type if the type already exists func (n *client) Static(s map[string]string) { for k, v := range s { n.statics[k] = v } } // AddRoute method is used to append a route onto the existing routes type if the type already exists func (n *client) AddRoute(group GinRouteGroup) { n.groups = append(n.groups, group) } // AddRoutes method is used to append multiple routes onto the existing routes type if the type already exists func (n *client) AddRoutes(groups []GinRouteGroup) { n.groups = append(n.groups, groups...) } // Use method will append any additional middleware onto the Gin router to be used within the app. // By default, this client uses the IdentityAuth and UUID middleware, all additional middleware will need // to be defined. func (n *client) Use(funcs ...gin.HandlerFunc) { n.middleware = append(n.middleware, funcs...) } // NewGin method is intended to provide an easy, reusable source // This method takes a client receiver and starts the Gin engine with the routes defined by the // NewGinRoutes function. func (n *client) NewGin() *gin.Engine { // Create the Default Engine with Gins Logger and Recovery middleware already attached e := gin.New() // Append on any additional middleware. This includes the auth and UUID middleware, including // any additional middleware attached with the client Use method e.Use(n.middleware...) // Generate the list of gin Groups and their subsequent Handlers for _, rt := range n.groups { // Initialize the group with the base path values base := e.Group(rt.BasePath) // Append on route group specific middleware base.Use(rt.Middleware...) // Iterate through the handlers and add them to the group for _, h := range rt.Methods { base.Handle(h.Method, h.RelativePath, h.Handlers) } } // Iterate through any static definitions, if any, and append them on // the route list for k, v := range n.statics { e.Static(k, v) } // If we are in Gin Release Mode, log the routes to terminal as they will not // reflect similar to debug mode if gin.Mode() == gin.ReleaseMode { for _, route := range e.Routes() { logrus.WithFields( logrus.Fields{ "path": route.Path, "method": route.Method, }, ).Info() } } // If a port has not already been pre-defined, define the default port if n.port == "" { n.SetAddress(":8080") } return e } // StartGin method is intended to provide an easy, reusable source // This method takes a client receiver and starts the Gin engine with the routes defined by the // NewGinRoutes function. func (n *client) StartGin() { // Create the Default Engine with Gins Logger and Recovery middleware already attached e := gin.New() // Append on any additional middleware. This includes the auth and UUID middleware, including // any additional middleware attached with the client Use method e.Use(n.middleware...) // Generate the list of gin Groups and their subsequent Handlers for _, rt := range n.groups { // Initialize the group with the base path values base := e.Group(rt.BasePath) // Append on route group specific middleware base.Use(rt.Middleware...) // Iterate through the handlers and add them to the group for _, h := range rt.Methods { base.Handle(h.Method, h.RelativePath, h.Handlers) } } for k, v := range n.statics { e.Static(k, v) } // If we are in Gin Release Mode, log the routes to terminal as they will not // reflect similar to debug mode if gin.Mode() == gin.ReleaseMode { for _, route := range e.Routes() { logrus.WithFields( logrus.Fields{ "path": route.Path, "method": route.Method, }, ).Info() } } // If a port has not already been pre-defined, define the default port if n.port == "" { n.SetAddress(":8080") } // Attach the Engine to the http.Server and start listening and serving HTTP requests e.Run(n.port) }