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 }