github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/workflow/task.go (about)

     1  package workflow
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"sync"
     8  	"time"
     9  
    10  	"database/sql"
    11  
    12  	dbconn "github.com/mdaxf/iac/databases"
    13  	"github.com/mdaxf/iac/documents"
    14  	"github.com/mdaxf/iac/logger"
    15  
    16  	"github.com/mdaxf/iac/com"
    17  	wftype "github.com/mdaxf/iac/workflow/types"
    18  
    19  	"github.com/mdaxf/iac/notifications"
    20  
    21  	"github.com/mdaxf/iac/framework/callback_mgr"
    22  )
    23  
    24  type WorkFlowTask struct {
    25  	WorkFlowTaskID int64
    26  	DocDBCon       *documents.DocDB
    27  	DBTx           *sql.Tx
    28  	Ctx            context.Context
    29  	CtxCancel      context.CancelFunc
    30  	UserName       string
    31  	ClientID       string
    32  	iLog           logger.Log
    33  }
    34  
    35  // NewWorkFlowTaskType creates a new instance of WorkFlowTask with the specified parameters.
    36  // It initializes the log, context, and other properties of the WorkFlowTask.
    37  // Parameters:
    38  // - workflowtaskID: The ID of the workflow task.
    39  // - UserName: The name of the user.
    40  // Returns:
    41  // - A pointer to the newly created WorkFlowTask instance.
    42  func NewWorkFlowTaskType(workflowtaskID int64, UserName string) *WorkFlowTask {
    43  	log := logger.Log{}
    44  	log.ModuleName = logger.Framework
    45  	log.ControllerName = "workflow Explosion"
    46  	log.User = UserName
    47  
    48  	Ctx, CtxCancel := context.WithTimeout(context.Background(), 10*time.Second)
    49  
    50  	return &WorkFlowTask{
    51  		WorkFlowTaskID: workflowtaskID,
    52  		DocDBCon:       documents.DocDBCon,
    53  		UserName:       UserName,
    54  		iLog:           log,
    55  		Ctx:            Ctx,
    56  		CtxCancel:      CtxCancel,
    57  	}
    58  }
    59  
    60  // UpdateTaskStatus updates the status of a workflow task.
    61  // It takes a newstatus parameter of type int64, representing the new status value.
    62  // The function returns an error if there was an issue updating the task status.
    63  // Otherwise, it returns nil.
    64  func (wft *WorkFlowTask) UpdateTaskStatus(newstatus int64) error {
    65  
    66  	startTime := time.Now()
    67  	defer func() {
    68  		elapsed := time.Since(startTime)
    69  		wft.iLog.PerformanceWithDuration("UpdateTaskStatus", elapsed)
    70  	}()
    71  
    72  	wft.iLog.Debug(fmt.Sprintf("UpdateTaskStatus by workflowtaskid: %d", wft.WorkFlowTaskID))
    73  
    74  	DBTx, err := dbconn.DB.Begin()
    75  
    76  	defer DBTx.Rollback()
    77  
    78  	if err != nil {
    79  		wft.iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
    80  		return err
    81  	}
    82  	dbop := dbconn.NewDBOperation(wft.UserName, DBTx, logger.Framework)
    83  	Columns := []string{"Status"}
    84  	Values := []string{fmt.Sprintf("%d", newstatus)}
    85  	datatypes := []int{int(1)}
    86  
    87  	if newstatus == 5 {
    88  		Columns = []string{"Status", "CompletedDate"}
    89  		Values = []string{fmt.Sprintf("%d", newstatus), time.Now().UTC().Format("2006-01-02 15:04:05")}
    90  		datatypes = []int{int(1), int(0)}
    91  	} else if newstatus == 2 {
    92  		Columns = []string{"Status", "StartedDate"}
    93  		Values = []string{fmt.Sprintf("%d", newstatus), time.Now().UTC().Format("2006-01-02 15:04:05")}
    94  		datatypes = []int{int(1), int(0)}
    95  	}
    96  
    97  	Where := fmt.Sprintf("ID = %d", wft.WorkFlowTaskID)
    98  	_, err = dbop.TableUpdate("workflow_tasks", Columns, Values, datatypes, Where)
    99  	if err != nil {
   100  		wft.iLog.Error(fmt.Sprintf("Error in updating workflow tasks: %s", err))
   101  		return err
   102  	}
   103  
   104  	DBTx.Commit()
   105  
   106  	return nil
   107  }
   108  
   109  // StartTask starts the workflow task.
   110  // It retrieves the workflow entity ID, workflow node ID, and notification UUID from the database based on the task ID.
   111  // It then updates the task status to 2 (indicating that the task has started).
   112  // If the task is being executed within an internal transaction, it commits the transaction.
   113  // Finally, it asynchronously updates the notification with the "Task Started" message.
   114  // Returns an error if any error occurs during the execution.
   115  
   116  func (wft *WorkFlowTask) StartTask() error {
   117  	startTime := time.Now()
   118  	defer func() {
   119  		elapsed := time.Since(startTime)
   120  		wft.iLog.PerformanceWithDuration("StartTask", elapsed)
   121  	}()
   122  
   123  	wft.iLog.Debug(fmt.Sprintf("StartTask by workflowtaskid: %d", wft.WorkFlowTaskID))
   124  
   125  	DBTx := wft.DBTx
   126  	err := error(nil)
   127  	internaltransaction := false
   128  
   129  	if DBTx == nil {
   130  		DBTx, err = dbconn.DB.Begin()
   131  		internaltransaction = true
   132  		defer DBTx.Rollback()
   133  
   134  		if err != nil {
   135  			wft.iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   136  			return err
   137  		}
   138  	}
   139  	dbop := dbconn.NewDBOperation(wft.UserName, DBTx, logger.Framework)
   140  
   141  	rows, err := dbop.Query_Json(fmt.Sprintf("select WorkflowEntityID, WorkflowNodeID, NotificationUUID from workflow_tasks where ID = %d", wft.WorkFlowTaskID))
   142  	if err != nil {
   143  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   144  		return err
   145  	}
   146  
   147  	if len(rows) == 0 {
   148  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   149  		return err
   150  	}
   151  
   152  	err = wft.UpdateTaskStatus(2)
   153  
   154  	if err != nil {
   155  		wft.iLog.Error(fmt.Sprintf("Update the task status to %d error!", 2))
   156  	}
   157  
   158  	if internaltransaction {
   159  		DBTx.Commit()
   160  	}
   161  
   162  	if rows[0]["NotificationUUID"] != nil {
   163  		go func() {
   164  
   165  			NotificationUUID := rows[0]["NotificationUUID"].(string)
   166  			notifications.UpdateNotificationbyUUID(NotificationUUID, wft.UserName, "Task Started")
   167  
   168  		}()
   169  	}
   170  
   171  	return nil
   172  
   173  }
   174  
   175  func (wft *WorkFlowTask) UpdateProcessData(extprocessdata map[string]interface{}) error {
   176  	startTime := time.Now()
   177  	defer func() {
   178  		elapsed := time.Since(startTime)
   179  		wft.iLog.PerformanceWithDuration("UpdateProcessData", elapsed)
   180  	}()
   181  
   182  	wft.iLog.Debug(fmt.Sprintf("UpdateProcessData by workflowtaskid: %d with new process data: %v", wft.WorkFlowTaskID, extprocessdata))
   183  
   184  	DBTx := wft.DBTx
   185  	err := error(nil)
   186  	internaltransaction := false
   187  
   188  	if DBTx == nil {
   189  		DBTx, err = dbconn.DB.Begin()
   190  		internaltransaction = true
   191  		defer DBTx.Rollback()
   192  
   193  		if err != nil {
   194  			wft.iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   195  			return err
   196  		}
   197  	}
   198  	dbop := dbconn.NewDBOperation(wft.UserName, DBTx, logger.Framework)
   199  
   200  	rows, err := dbop.Query_Json(fmt.Sprintf("select WorkflowEntityID, WorkflowNodeID, ProcessData, NotificationUUID from workflow_tasks where ID = %d", wft.WorkFlowTaskID))
   201  	if err != nil {
   202  		wft.iLog.Error(fmt.Sprintf("Error in getting cureent process data: %s", err))
   203  		return err
   204  	}
   205  
   206  	if len(rows) == 0 {
   207  		wft.iLog.Error(fmt.Sprintf("Error in getting cureent process data %s", err))
   208  		return err
   209  	}
   210  	ProcessData := map[string]interface{}{}
   211  
   212  	if rows[0]["ProcessData"] != nil {
   213  		err := json.Unmarshal([]byte(rows[0]["ProcessData"].(string)), &ProcessData)
   214  		if err != nil {
   215  			wft.iLog.Error(fmt.Sprintf("Error in getting process data: %s", err))
   216  			return err
   217  		}
   218  	}
   219  
   220  	wft.iLog.Debug(fmt.Sprintf("the current process data: %v", ProcessData))
   221  	for key, value := range extprocessdata {
   222  		ProcessData[key] = value
   223  	}
   224  
   225  	wft.iLog.Debug(fmt.Sprintf("the new process data: %v", ProcessData))
   226  
   227  	jsonData, err := json.Marshal(ProcessData)
   228  	if err != nil {
   229  		wft.iLog.Error(fmt.Sprintf("Error in WorkFlow.Explosion.explodeNode: %s", err))
   230  		return err
   231  	}
   232  
   233  	Columns := []string{"ProcessData"}
   234  	Values := []string{string(jsonData)}
   235  	datatypes := []int{int(0)}
   236  	Where := fmt.Sprintf("ID = %d", wft.WorkFlowTaskID)
   237  	_, err = dbop.TableUpdate("workflow_tasks", Columns, Values, datatypes, Where)
   238  	if err != nil {
   239  		wft.iLog.Error(fmt.Sprintf("Error in updating workflow tasks: %s", err))
   240  		return err
   241  	}
   242  
   243  	if internaltransaction {
   244  		DBTx.Commit()
   245  	}
   246  	return nil
   247  
   248  }
   249  
   250  func (wft *WorkFlowTask) ExecuteTaskTranCode(TranCode string) error {
   251  	startTime := time.Now()
   252  	defer func() {
   253  		elapsed := time.Since(startTime)
   254  		wft.iLog.PerformanceWithDuration("ExecuteTaskTranCode", elapsed)
   255  	}()
   256  
   257  	wft.iLog.Debug(fmt.Sprintf("ExecuteTaskTranCode by workflowtaskid: %d", wft.WorkFlowTaskID))
   258  
   259  	if TranCode == "" {
   260  		wft.iLog.Debug("Trancode canot be empty!")
   261  		return fmt.Errorf("Trancode is empty!")
   262  	}
   263  
   264  	DBTx := wft.DBTx
   265  	err := error(nil)
   266  	internaltransaction := false
   267  
   268  	if DBTx == nil {
   269  		DBTx, err = dbconn.DB.Begin()
   270  		internaltransaction = true
   271  		defer DBTx.Rollback()
   272  
   273  		if err != nil {
   274  			wft.iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   275  			return err
   276  		}
   277  	}
   278  	dbop := dbconn.NewDBOperation(wft.UserName, DBTx, logger.Framework)
   279  
   280  	rows, err := dbop.Query_Json(fmt.Sprintf("select WorkflowEntityID, WorkflowNodeID, ProcessData, NotificationUUID from workflow_tasks where ID = %d", wft.WorkFlowTaskID))
   281  	if err != nil {
   282  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   283  		return err
   284  	}
   285  
   286  	if len(rows) == 0 {
   287  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   288  		return err
   289  	}
   290  
   291  	//	var WorkflowEntityID int64 = 0
   292  	//	WorkflowNodeID := ""
   293  	ProcessData := map[string]interface{}{}
   294  	//	NotificationUUID := ""
   295  	/*
   296  		if rows[0]["WorkflowEntityID"] != nil {
   297  			WorkflowEntityID = rows[0]["WorkflowEntityID"].(int64)
   298  		}
   299  
   300  		if rows[0]["WorkflowNodeID"] != nil {
   301  			WorkflowNodeID = rows[0]["WorkflowNodeID"].(string)
   302  		}
   303  	*/
   304  	if rows[0]["ProcessData"] != nil {
   305  		err := json.Unmarshal([]byte(rows[0]["ProcessData"].(string)), &ProcessData)
   306  		if err != nil {
   307  			wft.iLog.Error(fmt.Sprintf("Error in getting process data: %s", err))
   308  			return err
   309  		}
   310  	}
   311  
   312  	_, err = ExecuteTaskTranCode(wft.WorkFlowTaskID, TranCode, ProcessData, DBTx, wft.DocDBCon, wft.UserName)
   313  
   314  	if err != nil {
   315  		wft.iLog.Error(fmt.Sprintf("Error during executing the trancode with error: %v", err))
   316  		return err
   317  	}
   318  
   319  	if internaltransaction {
   320  		DBTx.Commit()
   321  	}
   322  	return nil
   323  }
   324  
   325  // CompleteTask completes the workflow task.
   326  // It updates the status and completed date of the task in the database.
   327  // If the task is a gateway, it checks the routing table to determine the next nodes.
   328  // If the task is a task node, it follows the links to find the next nodes.
   329  // If the task is an end node, it marks the workflow as completed and triggers the validation and completion process.
   330  // After completing the task, it updates the notification associated with the task.
   331  // If the task is part of an internal transaction, it commits the transaction.
   332  // If there are next nodes, it spawns goroutines to handle each next node concurrently.
   333  // Returns an error if any operation fails.
   334  
   335  func (wft *WorkFlowTask) CompleteTask() error {
   336  
   337  	startTime := time.Now()
   338  	defer func() {
   339  		elapsed := time.Since(startTime)
   340  		wft.iLog.PerformanceWithDuration("CompleteTask", elapsed)
   341  	}()
   342  
   343  	wft.iLog.Debug(fmt.Sprintf("CompleteTask by workflowtaskid: %d", wft.WorkFlowTaskID))
   344  
   345  	DBTx := wft.DBTx
   346  	err := error(nil)
   347  	internaltransaction := false
   348  
   349  	if DBTx == nil {
   350  		DBTx, err = dbconn.DB.Begin()
   351  		internaltransaction = true
   352  		defer DBTx.Rollback()
   353  
   354  		if err != nil {
   355  			wft.iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   356  			return err
   357  		}
   358  	}
   359  	dbop := dbconn.NewDBOperation(wft.UserName, DBTx, logger.Framework)
   360  
   361  	Columns := []string{"Status", "CompletedDate"}
   362  	Values := []string{fmt.Sprintf("%d", 5), time.Now().UTC().Format("2006-01-02 15:04:05")}
   363  	datatypes := []int{int(1), int(0)}
   364  	Where := fmt.Sprintf("ID = %d", wft.WorkFlowTaskID)
   365  	_, err = dbop.TableUpdate("workflow_tasks", Columns, Values, datatypes, Where)
   366  	if err != nil {
   367  		wft.iLog.Error(fmt.Sprintf("Error in updating workflow tasks: %s", err))
   368  		return err
   369  	}
   370  
   371  	rows, err := dbop.Query_Json(fmt.Sprintf("select WorkflowEntityID, WorkflowNodeID, ProcessData, NotificationUUID from workflow_tasks where ID = %d", wft.WorkFlowTaskID))
   372  	if err != nil {
   373  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   374  		return err
   375  	}
   376  
   377  	if len(rows) == 0 {
   378  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   379  		return err
   380  	}
   381  
   382  	var WorkflowEntityID int64 = 0
   383  	WorkflowNodeID := ""
   384  	ProcessData := map[string]interface{}{}
   385  	NotificationUUID := ""
   386  
   387  	if rows[0]["WorkflowEntityID"] != nil {
   388  		WorkflowEntityID = rows[0]["WorkflowEntityID"].(int64)
   389  	}
   390  
   391  	if rows[0]["WorkflowNodeID"] != nil {
   392  		WorkflowNodeID = rows[0]["WorkflowNodeID"].(string)
   393  	}
   394  
   395  	if rows[0]["ProcessData"] != nil {
   396  		err := json.Unmarshal([]byte(rows[0]["ProcessData"].(string)), &ProcessData)
   397  		if err != nil {
   398  			wft.iLog.Error(fmt.Sprintf("Error in getting process data: %s", err))
   399  			return err
   400  		}
   401  	}
   402  
   403  	if rows[0]["NotificationUUID"] != nil {
   404  		NotificationUUID = rows[0]["NotificationUUID"].(string)
   405  	}
   406  
   407  	if WorkflowEntityID == 0 || WorkflowNodeID == "" {
   408  		err = fmt.Errorf("Error in getting workflow entity id: %s", err)
   409  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   410  		return err
   411  	}
   412  
   413  	rows, err = dbop.Query_Json(fmt.Sprintf("select WorkflowUUID from workflow_entities where ID = %d", WorkflowEntityID))
   414  	if err != nil {
   415  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow uuid: %s", err))
   416  		return err
   417  	}
   418  
   419  	if len(rows) == 0 {
   420  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow uuid: %s length of result is 0", err))
   421  		return err
   422  	}
   423  
   424  	var WorkFlow wftype.WorkFlow
   425  
   426  	WorkflowUUID := ""
   427  	if rows[0]["WorkflowUUID"] != nil {
   428  		WorkflowUUID = rows[0]["WorkflowUUID"].(string)
   429  	}
   430  	//WorkFlowSchema := rows[0]["WorkFlow"].([]byte)
   431  
   432  	if WorkflowUUID == "" {
   433  		err = fmt.Errorf("Error in getting workflow uuid: %s", err)
   434  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow uuid: %s", err))
   435  		return err
   436  	}
   437  
   438  	WorkFlow, _, err = GetWorkFlowbyUUID(WorkflowUUID, wft.UserName, *wft.DocDBCon)
   439  	//err = json.Unmarshal(WorkFlowSchema, &WorkFlow)
   440  	if err != nil {
   441  		wft.iLog.Error(fmt.Sprintf("Error in getting workflow schema: %s", err))
   442  		return err
   443  	}
   444  
   445  	Nodes := WorkFlow.Nodes
   446  	Links := WorkFlow.Links
   447  
   448  	// Get the current node routing table
   449  	currentNode := wftype.Node{}
   450  	for _, node := range Nodes {
   451  		if node.ID == WorkflowNodeID {
   452  			currentNode = node
   453  			break
   454  		}
   455  	}
   456  
   457  	nextNodes := []wftype.Node{}
   458  
   459  	//RoutingTable := currentNode.RoutingTable
   460  	/*
   461  		{
   462  			"sequence": 10,
   463  			"data": "Status",
   464  			"operator": "eq",
   465  			"value": "Approved",
   466  			"target": "nodeid"
   467  		}
   468  
   469  
   470  	*/
   471  	if currentNode.Type == "gateway" {
   472  		// check the routign table
   473  		RoutingTables := currentNode.RoutingTables
   474  		for _, routing := range RoutingTables {
   475  			if CheckRoutingCondition(routing, ProcessData) {
   476  				targetNodeID := routing.Target
   477  				for _, node := range Nodes {
   478  					if node.ID == targetNodeID {
   479  						nextNodes = append(nextNodes, node)
   480  						break
   481  					}
   482  				}
   483  				if len(nextNodes) == 0 {
   484  					err = fmt.Errorf("Error in getting next node with routing: %v", routing)
   485  					wft.iLog.Error(fmt.Sprintf("Error in getting next node: %s", err))
   486  					return err
   487  				}
   488  			}
   489  		}
   490  
   491  		if len(nextNodes) == 0 {
   492  			err = fmt.Errorf("Error in getting next node with routing table: %v", RoutingTables)
   493  			wft.iLog.Error(fmt.Sprintf("Error in getting next node: %s", err))
   494  			return err
   495  		}
   496  	} else if currentNode.Type == "task" {
   497  		for _, link := range Links {
   498  			if link.Source == WorkflowNodeID {
   499  				for _, node := range Nodes {
   500  					if node.ID == link.Target {
   501  						nextNodes = append(nextNodes, node)
   502  					}
   503  				}
   504  			}
   505  		}
   506  		if len(nextNodes) == 0 {
   507  			err = fmt.Errorf("Error in getting next node for the task: %v", currentNode)
   508  			wft.iLog.Error(fmt.Sprintf("Error in getting next node: %s", err))
   509  			return err
   510  		}
   511  
   512  	} else if currentNode.Type == "end" {
   513  		wft.iLog.Debug(fmt.Sprintf("Workflow completed for workflowtaskid: %d", wft.WorkFlowTaskID))
   514  
   515  		go func() {
   516  			ValidateAndCompleteWorkFlow(WorkflowEntityID, DBTx, wft.DocDBCon, wft.UserName)
   517  		}()
   518  	}
   519  
   520  	go func() {
   521  		notifications.UpdateNotificationbyUUID(NotificationUUID, wft.UserName, "Task Completed")
   522  	}()
   523  
   524  	if len(nextNodes) > 0 {
   525  
   526  		var wg sync.WaitGroup
   527  
   528  		// Add 1 to the wait group
   529  		wg.Add(1)
   530  
   531  		go ExplodeNextNodes(&wg, WorkflowEntityID, nextNodes, wft.UserName, ProcessData)
   532  
   533  		wg.Wait()
   534  	}
   535  
   536  	if internaltransaction {
   537  		DBTx.Commit()
   538  	}
   539  
   540  	return nil
   541  }
   542  
   543  // ExplodeNextNodes explodes the next nodes in a workflow task.
   544  // It creates new workflow tasks for each next node, associating them with the given WorkflowEntityID.
   545  // The function uses a sync.WaitGroup to wait for all tasks to complete before returning.
   546  // It also logs performance metrics and any errors that occur during the process.
   547  //
   548  // Parameters:
   549  // - wg: A pointer to a sync.WaitGroup used to wait for all tasks to complete.
   550  // - WorkflowEntityID: The ID of the workflow entity.
   551  // - nextNodes: A slice of wftype.Node representing the next nodes in the workflow.
   552  // - UserName: The name of the user performing the operation.
   553  //
   554  // Returns:
   555  // - An error if any error occurs during the process, otherwise nil.
   556  func ExplodeNextNodes(wg *sync.WaitGroup, WorkflowEntityID int64, nextNodes []wftype.Node, UserName string, ProcessData map[string]interface{}) error {
   557  
   558  	defer wg.Done()
   559  
   560  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "workflow tasks explode next node"}
   561  	startTime := time.Now()
   562  	defer func() {
   563  		elapsed := time.Since(startTime)
   564  		iLog.PerformanceWithDuration("ExplodeNextNode", elapsed)
   565  	}()
   566  
   567  	iLog.Debug(fmt.Sprintf("ExplodeNextNode by workflowentityid: %d", WorkflowEntityID))
   568  
   569  	err := error(nil)
   570  
   571  	idbTx, err := dbconn.DB.Begin()
   572  	if err != nil {
   573  		iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   574  		return err
   575  	}
   576  
   577  	defer idbTx.Rollback()
   578  
   579  	DocDBCon := documents.DocDBCon
   580  	//	defer DocDBCon.MongoDBClient.Disconnect(context.Background())
   581  
   582  	wfexplode := NewExplosion("", "", "", UserName, "")
   583  	for _, node := range nextNodes {
   584  		// create new workflow task
   585  		wfexplode.explodeNode(node, WorkflowEntityID, DocDBCon, idbTx, ProcessData)
   586  	}
   587  
   588  	idbTx.Commit()
   589  
   590  	return nil
   591  }
   592  
   593  // ValidateAndCompleteWorkFlow validates and completes a workflow based on the given parameters.
   594  // It takes the following parameters:
   595  // - WorkFlowEntityID: The ID of the workflow entity.
   596  // - idbTx: The SQL transaction object.
   597  // - DocDBCon: The document database connection object.
   598  // - UserName: The name of the user.
   599  // It returns a boolean value indicating whether the workflow was successfully completed, and an error if any.
   600  // If the workflow was successfully completed, it returns true, otherwise false.
   601  
   602  func ValidateAndCompleteWorkFlow(WorkFlowEntityID int64, idbTx *sql.Tx, DocDBCon *documents.DocDB, UserName string) (bool, error) {
   603  	iLog := logger.Log{ModuleName: logger.Framework, User: UserName, ControllerName: "ValidateAndCompleteWorkFlow"}
   604  	startTime := time.Now()
   605  	defer func() {
   606  		elapsed := time.Since(startTime)
   607  		iLog.PerformanceWithDuration("ValidateAndCompleteWorkFlow", elapsed)
   608  	}()
   609  
   610  	iLog.Debug(fmt.Sprintf("ValidateAndCompleteWorkFlow by workflowentityid: %d", WorkFlowEntityID))
   611  
   612  	internaltransaction := false
   613  	err := error(nil)
   614  
   615  	if idbTx == nil {
   616  		idbTx, err = dbconn.DB.Begin()
   617  		if err != nil {
   618  			iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   619  			return false, err
   620  		}
   621  		internaltransaction = true
   622  		defer idbTx.Rollback()
   623  	}
   624  
   625  	if DocDBCon == nil {
   626  		DocDBCon = documents.DocDBCon
   627  		//		defer DocDBCon.MongoDBClient.Disconnect(context.Background())
   628  	}
   629  
   630  	dbop := dbconn.NewDBOperation(UserName, idbTx, logger.Framework)
   631  
   632  	tasks, err := dbop.Query_Json(fmt.Sprintf("select * from workflow_tasks where WorkflowEntityID = %d AND Status != %d", WorkFlowEntityID, 5))
   633  	if err != nil {
   634  		iLog.Error(fmt.Sprintf("Error in getting workflow tasks: %s", err))
   635  
   636  		if internaltransaction {
   637  			idbTx.Commit()
   638  		}
   639  
   640  		return false, err
   641  	}
   642  
   643  	if len(tasks) == 0 {
   644  		iLog.Debug(fmt.Sprintf("Workflow completed for workflowentityid: %d", WorkFlowEntityID))
   645  
   646  		Columns := []string{"Status", "CompletedDate"}
   647  		Values := []string{fmt.Sprintf("%d", 5), time.Now().UTC().Format("2006-01-02 15:04:05")}
   648  		datatypes := []int{int(1), int(0)}
   649  		Where := fmt.Sprintf("ID = %d", WorkFlowEntityID)
   650  		_, err = dbop.TableUpdate("workflow_entities", Columns, Values, datatypes, Where)
   651  
   652  		if err != nil {
   653  			iLog.Error(fmt.Sprintf("Error in updating workflow entities: %s", err))
   654  			return false, err
   655  		}
   656  		if internaltransaction {
   657  			idbTx.Commit()
   658  		}
   659  		return true, nil
   660  	} else {
   661  		iLog.Debug(fmt.Sprintf("Workflow not completed for workflowentityid: %d", WorkFlowEntityID))
   662  
   663  		if internaltransaction {
   664  			idbTx.Commit()
   665  		}
   666  
   667  		return false, nil
   668  	}
   669  
   670  }
   671  
   672  // CheckRoutingCondition checks the routing condition based on the provided RoutingTable and ProcessData.
   673  // It returns true if the routing condition is met, otherwise false.
   674  // The routing condition is met if the ProcessData contains the data specified in the RoutingTable and the value of the data is equal to the value specified in the RoutingTable.
   675  // If the RoutingTable is the default routing table, it returns true.
   676  // If the ProcessData does not contain the data specified in the RoutingTable, it returns false.
   677  func CheckRoutingCondition(Routing wftype.RoutingTable, ProcessData map[string]interface{}) bool {
   678  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "workflow tasks check routing condition"}
   679  	startTime := time.Now()
   680  	defer func() {
   681  		elapsed := time.Since(startTime)
   682  		iLog.PerformanceWithDuration("CheckRoutingCondition", elapsed)
   683  	}()
   684  
   685  	iLog.Debug(fmt.Sprintf("CheckRoutingCondition by routing: %v with data %v", Routing, ProcessData))
   686  
   687  	if Routing.Default {
   688  		return true
   689  	}
   690  
   691  	if ProcessData[Routing.Data] == nil {
   692  		return false
   693  	} else if ProcessData[Routing.Data] == Routing.Value {
   694  		return true
   695  	} else {
   696  		return false
   697  	}
   698  
   699  }
   700  
   701  // ExecuteTask executes a workflow task.
   702  // It takes the following parameters:
   703  // - workflowtaskid: the ID of the workflow task
   704  // - NodeData: the data associated with the workflow task node
   705  // - idbTx: the SQL transaction object
   706  // - DocDBCon: the document database connection object
   707  // - UserName: the name of the user executing the task
   708  // It returns a map[string]interface{} and an error.
   709  // The map[string]interface{} contains the result of the task execution.
   710  func ExecuteTask(workflowtaskid int64, NodeData wftype.Node, idbTx *sql.Tx, DocDBCon *documents.DocDB, UserName string) (map[string]interface{}, error) {
   711  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "workflow tasks execution"}
   712  	startTime := time.Now()
   713  	defer func() {
   714  		elapsed := time.Since(startTime)
   715  		iLog.PerformanceWithDuration("ExecuteTaskTranCode", elapsed)
   716  	}()
   717  
   718  	iLog.Debug(fmt.Sprintf("ExecuteTask by workflowtaskid: %d", workflowtaskid))
   719  
   720  	if NodeData.Type == "start" {
   721  		wft := NewWorkFlowTaskType(workflowtaskid, UserName)
   722  		wft.DBTx = idbTx
   723  		wft.DocDBCon = DocDBCon
   724  		wft.UpdateTaskStatus(2) // In Progress / started
   725  		wft.CompleteTask()
   726  		return nil, nil
   727  	} else if NodeData.Type == "end" {
   728  		wft := NewWorkFlowTaskType(workflowtaskid, UserName)
   729  		wft.DBTx = idbTx
   730  		wft.DocDBCon = DocDBCon
   731  		wft.UpdateTaskStatus(2) // In Progress / started
   732  		wft.CompleteTask()
   733  		return nil, nil
   734  	}
   735  
   736  	if NodeData.Page == "" {
   737  
   738  		internaltransaction := false
   739  		err := error(nil)
   740  
   741  		if idbTx == nil {
   742  			idbTx, err = dbconn.DB.Begin()
   743  			if err != nil {
   744  				iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   745  				return nil, err
   746  			}
   747  			internaltransaction = true
   748  			defer idbTx.Rollback()
   749  		}
   750  
   751  		//	internalDoctransaction := false
   752  		if DocDBCon == nil {
   753  			DocDBCon = documents.DocDBCon
   754  			//	internalDoctransaction = true
   755  			defer DocDBCon.MongoDBClient.Disconnect(context.Background())
   756  		}
   757  
   758  		wft := NewWorkFlowTaskType(workflowtaskid, UserName)
   759  		wft.DBTx = idbTx
   760  		wft.DocDBCon = DocDBCon
   761  
   762  		wft.UpdateTaskStatus(2) // In Progress / started
   763  
   764  		if NodeData.TranCode != "" {
   765  
   766  			//_, err = callback_mgr.CallBackFunc("TranCode_Execute", workflowtaskid, NodeData.TranCode, NodeData.ProcessData, idbTx, DocDBCon, UserName)
   767  
   768  			_, err = ExecuteTaskTranCode(workflowtaskid, NodeData.TranCode, NodeData.ProcessData, idbTx, DocDBCon, UserName)
   769  
   770  			if err != nil {
   771  				wft.UpdateTaskStatus(4) // executed with Error
   772  				return nil, err
   773  			}
   774  		}
   775  		wft.CompleteTask()
   776  
   777  		if internaltransaction {
   778  			idbTx.Commit()
   779  		}
   780  	}
   781  
   782  	return nil, nil
   783  }
   784  
   785  // ExecuteTaskTranCode executes a transaction code for a workflow task.
   786  // It takes the workflow task ID, transaction code name, data map, database transaction, document database connection, and user name as input parameters.
   787  // It returns the result of the transaction code execution and an error, if any.
   788  // The result of the transaction code execution is a map[string]interface{}.
   789  func ExecuteTaskTranCode(workflowtaskid int64, TranCodeName string, data map[string]interface{}, idbTx *sql.Tx, DocDBCon *documents.DocDB, UserName string) (map[string]interface{}, error) {
   790  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "workflow tasks execute tran code"}
   791  	startTime := time.Now()
   792  	defer func() {
   793  		elapsed := time.Since(startTime)
   794  		iLog.PerformanceWithDuration("ExecuteTaskTranCode", elapsed)
   795  	}()
   796  
   797  	defer func() {
   798  		if r := recover(); r != nil {
   799  			iLog.Error(fmt.Sprintf("Recovered in ExecuteTaskTranCode: %s", r))
   800  		}
   801  	}()
   802  
   803  	iLog.Debug(fmt.Sprintf("Execute the Trancode for workflowtaskid: %d", workflowtaskid))
   804  
   805  	internaltransaction := false
   806  	err := error(nil)
   807  
   808  	if idbTx == nil {
   809  		idbTx, err = dbconn.DB.Begin()
   810  		if err != nil {
   811  			iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   812  			return nil, err
   813  		}
   814  		internaltransaction = true
   815  		defer idbTx.Rollback()
   816  	}
   817  
   818  	//	internalDoctransaction := false
   819  	if DocDBCon == nil {
   820  		DocDBCon = documents.DocDBCon
   821  		//	internalDoctransaction = true
   822  		defer DocDBCon.MongoDBClient.Disconnect(context.Background())
   823  	}
   824  
   825  	//data["WorkFlowTaskID"] = workflowtaskid
   826  	data["workflow_taskid"] = workflowtaskid
   827  	sc := com.IACMessageBusClient
   828  
   829  	//result, err := trancode.ExecutebyExternal(TranCodeName, data, idbTx, DocDBCon, sc)
   830  	result, err := callback_mgr.CallBackFunc("TranCode_Execute", TranCodeName, data, idbTx, DocDBCon, sc)
   831  	if err != nil {
   832  		iLog.Error(fmt.Sprintf("Error in executing tran code: %s", err))
   833  		return nil, err
   834  	}
   835  
   836  	dbop := dbconn.NewDBOperation(UserName, idbTx, logger.Framework)
   837  
   838  	rows, err := dbop.Query_Json(fmt.Sprintf("select WorkflowEntityID, WorkflowNodeID, ProcessData from workflow_tasks where ID = %d", workflowtaskid))
   839  	if err != nil {
   840  		iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   841  		return nil, err
   842  	}
   843  
   844  	if len(rows) == 0 {
   845  		iLog.Error(fmt.Sprintf("Error in getting workflow entity id: %s", err))
   846  		return nil, err
   847  	}
   848  
   849  	ProcessData := rows[0]["ProcessData"].(map[string]interface{})
   850  
   851  	outputs := callback_mgr.ConvertSliceToMap(result)
   852  	for key, value := range outputs {
   853  		ProcessData[key] = value
   854  	}
   855  
   856  	Columns := []string{"ProcessData"}
   857  	Values := []string{fmt.Sprintf("%s", ProcessData)}
   858  	datatypes := []int{int(0)}
   859  	Where := fmt.Sprintf("ID = %d", workflowtaskid)
   860  	_, err = dbop.TableUpdate("workflow_tasks", Columns, Values, datatypes, Where)
   861  	if err != nil {
   862  		iLog.Error(fmt.Sprintf("Error in updating workflow tasks: %s", err))
   863  		return nil, err
   864  	}
   865  
   866  	if internaltransaction {
   867  		idbTx.Commit()
   868  	}
   869  
   870  	//if internalDoctransaction {
   871  	//	DocDBCon.Commit()
   872  	//}
   873  
   874  	return outputs, nil
   875  }
   876  
   877  // GetWorkFlowTasks retrieves the workflow tasks associated with a given workflow entity ID and user name.
   878  // It returns a slice of maps, where each map represents a workflow task with its corresponding attributes.
   879  // If an error occurs during the retrieval process, it returns nil and the error.
   880  // The attributes of a workflow task are:
   881  // - ID: the ID of the workflow task
   882  // - WorkflowEntityID: the ID of the workflow entity
   883  // - WorkflowNodeID: the ID of the workflow node
   884  // - ProcessData: the data associated with the workflow task
   885  // - Status: the status of the workflow task
   886  // - CreatedOn: the date and time when the workflow task was created
   887  // - StartedDate: the date and time when the workflow task was started
   888  // - CompletedDate: the date and time when the workflow task was completed
   889  // - NotificationUUID: the UUID of the notification associated with the workflow task
   890  
   891  func GetWorkFlowTasks(workflowentityid int64, UserName string) ([]map[string]interface{}, error) {
   892  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "workflow tasks"}
   893  	startTime := time.Now()
   894  	defer func() {
   895  		elapsed := time.Since(startTime)
   896  		iLog.PerformanceWithDuration("GetWorkFlowTasks", elapsed)
   897  	}()
   898  
   899  	iLog.Debug(fmt.Sprintf("GetWorkFlowTasks by workflowentityid: %d", workflowentityid))
   900  
   901  	DBTx, err := dbconn.DB.Begin()
   902  
   903  	defer DBTx.Rollback()
   904  
   905  	if err != nil {
   906  		iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   907  		return nil, err
   908  	}
   909  
   910  	dbop := dbconn.NewDBOperation(UserName, DBTx, logger.Framework)
   911  
   912  	// Get workflow entity
   913  	result, err := dbop.Query_Json(fmt.Sprintf("select * from workflow_tasks where WorkflowEntityID = %d", workflowentityid))
   914  
   915  	if err != nil {
   916  		iLog.Error(fmt.Sprintf("Error in getting workflow tasks: %s", err))
   917  		return nil, err
   918  	}
   919  
   920  	DBTx.Commit()
   921  
   922  	return result, nil
   923  }
   924  
   925  func GetTasksbyUser(UserName string) ([]map[string]interface{}, error) {
   926  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "workflow tasks"}
   927  	startTime := time.Now()
   928  	defer func() {
   929  		elapsed := time.Since(startTime)
   930  		iLog.PerformanceWithDuration("GetTasksbyUser", elapsed)
   931  	}()
   932  
   933  	iLog.Debug(fmt.Sprintf("GetTasksbyUser by username: %s", UserName))
   934  
   935  	DBTx, err := dbconn.DB.Begin()
   936  
   937  	defer DBTx.Rollback()
   938  
   939  	if err != nil {
   940  		iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   941  		return nil, err
   942  	}
   943  
   944  	dbop := dbconn.NewDBOperation(UserName, DBTx, logger.Framework)
   945  
   946  	// Get workflow entity
   947  	result, err := dbop.Query_Json(fmt.Sprintf("SELECT * FROM workflow_tasks wt WHERE exists (Select 1 FROM workflow_task_assignments wts LEFT JOIN user_roles ur on ur.RoleID = wts.RoleID INNER JOIN users u on u.ID = wts.UserID OR ur.UserID = u.ID where wts.workflowTaskID = wt.ID AND u.LoginName = '%s')", UserName))
   948  
   949  	if err != nil {
   950  		iLog.Error(fmt.Sprintf("Error in getting workflow tasks: %s", err))
   951  		return nil, err
   952  	}
   953  
   954  	DBTx.Commit()
   955  
   956  	return result, nil
   957  }
   958  
   959  func GetTaskPreTaskData(taskid int64, UserName string) (map[string]interface{}, error) {
   960  
   961  	iLog := logger.Log{ModuleName: logger.Framework, ControllerName: "GetTaskPreTaskData"}
   962  	startTime := time.Now()
   963  	defer func() {
   964  		elapsed := time.Since(startTime)
   965  		iLog.PerformanceWithDuration("GetTaskPreTaskData", elapsed)
   966  	}()
   967  
   968  	iLog.Debug(fmt.Sprintf("GetTaskPreTaskData by username: %s", UserName))
   969  
   970  	DBTx, err := dbconn.DB.Begin()
   971  
   972  	defer DBTx.Rollback()
   973  
   974  	if err != nil {
   975  		iLog.Error(fmt.Sprintf("Error in creating DB connection: %s", err))
   976  		return nil, err
   977  	}
   978  
   979  	dbop := dbconn.NewDBOperation(UserName, DBTx, logger.Framework)
   980  
   981  	// Get workflow entity
   982  	rows, err := dbop.Query_Json(fmt.Sprintf("SELECT PreTaskData FROM workflow_tasks WHERE id = %d", taskid))
   983  
   984  	if err != nil {
   985  		iLog.Error(fmt.Sprintf("Error in getting workflow task's PreTaskData: %s", err))
   986  		return nil, err
   987  	}
   988  
   989  	if len(rows) == 0 {
   990  		iLog.Error(fmt.Sprintf("Error in getting workflow uuid: %s length of result is 0", err))
   991  		return nil, err
   992  	}
   993  
   994  	PreTaskData := make(map[string]interface{})
   995  
   996  	if rows[0]["PreTaskData"] != nil {
   997  		err := json.Unmarshal([]byte(rows[0]["PreTaskData"].(string)), &PreTaskData)
   998  		if err != nil {
   999  			iLog.Error(fmt.Sprintf("Error in getting pretask data: %s", err))
  1000  			return nil, err
  1001  		}
  1002  	}
  1003  
  1004  	DBTx.Commit()
  1005  
  1006  	return PreTaskData, nil
  1007  
  1008  }