github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/contollershandlers.go (about) 1 // Copyright 2023 IAC. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "fmt" 19 "net/http" 20 "reflect" 21 "strings" 22 "time" 23 24 // "github.com/gin-contrib/timeout" 25 config "github.com/mdaxf/iac/config" 26 27 "github.com/gin-gonic/gin" 28 "github.com/mdaxf/iac/controllers/iacai" 29 "github.com/mdaxf/iac/controllers/bpmcontroller" 30 "github.com/mdaxf/iac/controllers/collectionop" 31 "github.com/mdaxf/iac/controllers/component" 32 "github.com/mdaxf/iac/controllers/databaseop" 33 "github.com/mdaxf/iac/controllers/function" 34 healthcheck "github.com/mdaxf/iac/controllers/health" 35 "github.com/mdaxf/iac/controllers/lngcodes" 36 "github.com/mdaxf/iac/controllers/notifications" 37 "github.com/mdaxf/iac/controllers/role" 38 "github.com/mdaxf/iac/controllers/trans" 39 "github.com/mdaxf/iac/controllers/user" 40 "github.com/mdaxf/iac/controllers/workflow" 41 "github.com/mdaxf/iac/framework/auth" 42 ) 43 44 // loadControllers loads the specified controllers into the router. 45 // It iterates over the controllers and calls createEndpoints to create the endpoints for each controller. 46 // The performance duration of the function is logged using ilog.PerformanceWithDuration. 47 // If an error occurs while loading a controller module, an error message is logged using ilog.Error. 48 // The function returns the error returned by createEndpoints. 49 // The function is called by main. 50 func loadControllers(router *gin.Engine, controllers []config.Controller) { 51 52 startTime := time.Now() 53 defer func() { 54 elapsed := time.Since(startTime) 55 ilog.PerformanceWithDuration("main.loadControllers", elapsed) 56 }() 57 58 for _, controllerConfig := range controllers { 59 ilog.Info(fmt.Sprintf("loadControllers:%s", controllerConfig.Module)) 60 61 err := createEndpoints(router, controllerConfig.Module, controllerConfig.Path, controllerConfig.Endpoints, controllerConfig) 62 if err != nil { 63 ilog.Error(fmt.Sprintf("Failed to load controller module %s: %v", controllerConfig.Module, err)) 64 } 65 } 66 } 67 68 // getModule returns a reflect.Value of the specified module. 69 // It measures the performance of the function and logs the elapsed time. 70 // The module parameter specifies the name of the module to retrieve. 71 // The function returns a reflect.Value of the module instance. 72 // If the module is not found, it returns an empty reflect.Value. 73 // The function is called by loadControllers. 74 func getModule(module string) reflect.Value { 75 76 startTime := time.Now() 77 defer func() { 78 elapsed := time.Since(startTime) 79 ilog.PerformanceWithDuration("main.getModule", elapsed) 80 }() 81 82 ilog.Info(fmt.Sprintf("loadControllers get controller instance:%s", module)) 83 84 switch module { 85 case "RoleController": 86 moduleInstance := &role.RoleController{} 87 return reflect.ValueOf(moduleInstance) 88 89 case "UserController": 90 moduleInstance := &user.UserController{} 91 return reflect.ValueOf(moduleInstance) 92 93 case "TranCodeController": 94 moduleInstance := &trans.TranCodeController{} 95 return reflect.ValueOf(moduleInstance) 96 97 case "CollectionController": 98 moduleInstance := &collectionop.CollectionController{} 99 return reflect.ValueOf(moduleInstance) 100 case "DBController": 101 moduleInstance := &databaseop.DBController{} 102 return reflect.ValueOf(moduleInstance) 103 case "FunctionController": 104 moduleInstance := &function.FunctionController{} 105 return reflect.ValueOf(moduleInstance) 106 case "LCController": 107 moduleInstance := &lngcodes.LCController{} 108 return reflect.ValueOf(moduleInstance) 109 case "HealthController": 110 moduleInstance := &healthcheck.HealthController{} 111 return reflect.ValueOf(moduleInstance) 112 case "NotificationController": 113 moduleInstance := ¬ifications.NotificationController{} 114 return reflect.ValueOf(moduleInstance) 115 116 case "WorkFlowController": 117 moduleInstance := &workflow.WorkFlowController{} 118 return reflect.ValueOf(moduleInstance) 119 120 case "BPMController": 121 moduleInstance := &bpmcontroller.BPMController{} 122 return reflect.ValueOf(moduleInstance) 123 124 case "IACComponentController": 125 moduleInstance := &component.IACComponentController{} 126 return reflect.ValueOf(moduleInstance) 127 128 case "IACAIController": 129 moduleInstance := &iacai.IACAIController{} 130 return reflect.ValueOf(moduleInstance) 131 } 132 133 return reflect.Value{} 134 } 135 136 // createEndpoints creates API endpoints for a given module using the provided router. 137 // It takes the module name, module path, list of endpoints, and controller configuration as input. 138 // The function adds the API endpoints to the router based on the HTTP method specified for each endpoint. 139 // It also applies authentication middleware to the appropriate endpoints. 140 // The function returns an error if there is any issue creating the endpoints. 141 // The function is called by loadControllers. 142 func createEndpoints(router *gin.Engine, module string, modulepath string, endpoints []config.Endpoint, controllercfg config.Controller) error { 143 144 startTime := time.Now() 145 defer func() { 146 elapsed := time.Since(startTime) 147 ilog.PerformanceWithDuration("main.createEndpoints", elapsed) 148 }() 149 150 ilog.Info(fmt.Sprintf("Create the endpoints for the module:%s", module)) 151 152 //moduleValue := reflect.ValueOf(module) 153 154 moduleValue := getModule(module) 155 156 for _, endpoint := range endpoints { 157 // Get the handler function for the endpoint method 158 159 //handlermethod := reflect.ValueOf(moduleValue).MethodByName(endpoint.Handler); 160 161 handler, err := getHandlerFunc(moduleValue, endpoint.Handler, controllercfg) 162 if err != nil { 163 return fmt.Errorf("error creating endpoint '%s': %v", endpoint.Path, err) 164 } 165 ilog.Debug(fmt.Sprintf("modulepath:%s, method:%s module:%s", modulepath, endpoint.Method, module)) 166 // Add the API endpoint to the router 167 //auth.AuthMiddleware(), 168 169 switch endpoint.Method { 170 case http.MethodGet: 171 router.Use(auth.AuthMiddleware()) 172 router.GET(fmt.Sprintf("%s/%s", modulepath, endpoint.Path), handler) 173 case http.MethodPost: 174 175 if strings.Contains(modulepath, "/user/login") { 176 router.POST(fmt.Sprintf("%s/%s", modulepath, endpoint.Path), handler) 177 } else { 178 router.Use(auth.AuthMiddleware()) 179 router.POST(fmt.Sprintf("%s/%s", modulepath, endpoint.Path), handler) 180 } 181 case http.MethodPut: 182 router.Use(auth.AuthMiddleware()) 183 router.PUT(fmt.Sprintf("%s/%s", modulepath, endpoint.Path), handler) 184 case http.MethodPatch: 185 router.Use(auth.AuthMiddleware()) 186 router.PATCH(fmt.Sprintf("%s/%s", modulepath, endpoint.Path), handler) 187 case http.MethodDelete: 188 router.Use(auth.AuthMiddleware()) 189 router.DELETE(fmt.Sprintf("%s/%s", modulepath, endpoint.Path), handler) 190 default: 191 return fmt.Errorf("unsupported HTTP method '%s'", endpoint.Method) 192 } 193 } 194 195 return nil 196 } 197 198 // getHandlerFunc is a function that returns a gin.HandlerFunc based on the provided module, name, and controller configuration. 199 // It measures the performance duration of the handler function and recovers from any panics that occur. 200 // If the controller configuration specifies a timeout, the handler function is executed with a timeout. 201 // The handler function takes a *gin.Context as input and returns an HTTP response. 202 // If an error occurs during the execution of the handler function, it is returned as an HTTP error response. 203 // If the handler function returns a []byte, it is returned as the response body with the "application/json" content type. 204 // If no error or data is returned, the handler function sets the HTTP status code to 200. 205 // If the module value is invalid or the method name is invalid, an error is returned. 206 // The function is called by createEndpoints. 207 func getHandlerFunc(module reflect.Value, name string, controllercfg config.Controller) (gin.HandlerFunc, error) { 208 startTime := time.Now() 209 defer func() { 210 elapsed := time.Since(startTime) 211 ilog.PerformanceWithDuration("main.getHandlerFunc", elapsed) 212 }() 213 214 /* defer func() { 215 if r := recover(); r != nil { 216 ilog.Error(fmt.Sprintf("Panic: %s", r)) 217 return 218 } 219 }() 220 */ 221 ilog.Info(fmt.Sprintf("Get Handler Function:%s", name)) 222 223 if module.Kind() != reflect.Ptr || module.IsNil() { 224 return nil, fmt.Errorf("invalid module value: %v", module) 225 } 226 227 method := module.MethodByName(name) 228 if !method.IsValid() { 229 return nil, fmt.Errorf("invalid method name: %s", name) 230 } 231 232 if controllercfg.Timeout > 0 { 233 return func(c *gin.Context) { 234 in := make([]reflect.Value, 1) 235 in[0] = reflect.ValueOf(c) 236 out, err := callWithTimeout(method, in, time.Duration(controllercfg.Timeout)*time.Millisecond) 237 if err != nil { 238 c.AbortWithError(http.StatusInternalServerError, err) 239 return 240 } 241 if len(out) > 0 { 242 if err, ok := out[0].Interface().(error); ok { 243 c.AbortWithError(http.StatusInternalServerError, err) 244 return 245 } 246 if data, ok := out[0].Interface().([]byte); ok { 247 c.Data(http.StatusOK, "application/json", data) 248 return 249 } 250 } 251 c.Status(http.StatusOK) 252 }, nil 253 } else { 254 return func(c *gin.Context) { 255 256 defer func() { 257 if r := recover(); r != nil { 258 ilog.Error(fmt.Sprintf("Panic: %s", r)) 259 c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Panic: %s", r)) 260 return 261 } 262 }() 263 264 in := make([]reflect.Value, 1) 265 in[0] = reflect.ValueOf(c) 266 out := method.Call(in) 267 if len(out) > 0 { 268 if err, ok := out[0].Interface().(error); ok { 269 ilog.Error(fmt.Sprintf("%s %s Error: %s", module, name, err)) 270 c.AbortWithError(http.StatusInternalServerError, err) 271 return 272 } 273 if data, ok := out[0].Interface().([]byte); ok { 274 ilog.Debug(fmt.Sprintf("%s %s Data: %s", module, name, data)) 275 c.Data(http.StatusOK, "application/json", data) 276 return 277 } 278 } 279 c.Status(http.StatusOK) 280 }, nil 281 } 282 } 283 284 // callWithTimeout is a function that executes a given method with a timeout. 285 // It takes a reflect.Value representing the method to be called, an array of reflect.Value arguments, 286 // and a timeout duration. It returns an array of reflect.Value results and an error. 287 // If the method execution completes within the timeout, the results are returned. 288 // If the timeout is exceeded, an error is returned. 289 // The function is called by getHandlerFunc. 290 func callWithTimeout(method reflect.Value, args []reflect.Value, timeout time.Duration) ([]reflect.Value, error) { 291 startTime := time.Now() 292 defer func() { 293 elapsed := time.Since(startTime) 294 ilog.PerformanceWithDuration(fmt.Sprintf("main.callWithTimeout: %v", method), elapsed) 295 }() 296 297 resultChan := make(chan []reflect.Value, 1) 298 //errChan := make(chan error, 1) 299 300 go func() { 301 result := method.Call(args) 302 resultChan <- result 303 }() 304 305 select { 306 case result := <-resultChan: 307 ilog.Debug(fmt.Sprintf("callWithTimeout result: %s", result)) 308 return result, nil 309 case <-time.After(timeout): 310 ilog.Error(fmt.Sprintf("callWithTimeout timeout: %s", timeout)) 311 return nil, fmt.Errorf("timeout exceeded") 312 } 313 }