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 := &notifications.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  }