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

     1  package trancode
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"time"
     9  
    10  	"database/sql"
    11  
    12  	dbconn "github.com/mdaxf/iac/databases"
    13  	"github.com/mdaxf/iac/documents"
    14  
    15  	funcgroup "github.com/mdaxf/iac/engine/funcgroup"
    16  
    17  	"github.com/mdaxf/iac/engine/types"
    18  	"github.com/mdaxf/iac/logger"
    19  	"go.mongodb.org/mongo-driver/bson"
    20  
    21  	"github.com/mdaxf/iac-signalr/signalr"
    22  	"github.com/mdaxf/iac/com"
    23  	tcom "github.com/mdaxf/iac/engine/com"
    24  	"github.com/mdaxf/iac/framework/callback_mgr"
    25  )
    26  
    27  type TranFlow struct {
    28  	Tcode           types.TranCode
    29  	DBTx            *sql.Tx
    30  	Ctx             context.Context
    31  	CtxCancel       context.CancelFunc
    32  	Externalinputs  map[string]interface{} // {sessionanme: value}
    33  	externaloutputs map[string]interface{} // {sessionanme: value}
    34  	SystemSession   map[string]interface{}
    35  	ilog            logger.Log
    36  	DocDBCon        *documents.DocDB
    37  	SignalRClient   signalr.Client
    38  	ErrorMessage    string
    39  	TestwithSc      bool
    40  	TestResults     map[string]interface{}
    41  }
    42  
    43  func Execute(trancode string, data map[string]interface{}, systemsessions map[string]interface{}) (map[string]interface{}, error) {
    44  	iLog := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TransCode"}
    45  	startTime := time.Now()
    46  	defer func() {
    47  		elapsed := time.Since(startTime)
    48  		iLog.PerformanceWithDuration("engine.TranCode.Execute", elapsed)
    49  	}()
    50  
    51  	defer func() {
    52  		if err := recover(); err != nil {
    53  			iLog.Error(fmt.Sprintf("There is error to engine.TranCode.Execute with error: %s", err))
    54  			//	f.ErrorMessage = fmt.Sprintf("There is error to engine.funcs.ThrowErrorFuncs.Execute with error: %s", err)
    55  
    56  		}
    57  	}()
    58  
    59  	tranobj, err := getTranCodeData(trancode, documents.DocDBCon)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	tf := NewTranFlow(tranobj, data, systemsessions, nil, nil)
    64  
    65  	if callback_mgr.CallBackMap["TranCode_Execute"] == nil {
    66  		iLog.Debug("Register the trancode execution interface")
    67  		tfr := TranFlowstr{}
    68  		callback_mgr.RegisterCallBack("TranCode_Execute", tfr.Execute)
    69  
    70  	}
    71  
    72  	return tf.Execute()
    73  
    74  }
    75  
    76  // ExecuteUnitTest executes a unit test for a given trancode with the provided systemsessions.
    77  // It returns a map[string]interface{} containing the result of the unit test and an error, if any.
    78  // The result contains the following fields:
    79  // - Name: the name of the unit test
    80  // - Inputs: the inputs of the unit test
    81  // - ExpectedOutputs: the expected outputs of the unit test
    82  // - ExpectError: a boolean value indicating whether the unit test is expected to return an error
    83  // - ExpectedError: the expected error message
    84  // - ActualOutputs: the actual outputs of the unit test
    85  // - ActualError: the actual error message
    86  // - Result: the result of the unit test, either "Pass" or "Fail"
    87  
    88  func ExecuteUnitTest(trancode string, systemsessions map[string]interface{}) (map[string]interface{}, error) {
    89  	iLog := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TransCode"}
    90  	startTime := time.Now()
    91  	defer func() {
    92  		elapsed := time.Since(startTime)
    93  		iLog.PerformanceWithDuration("engine.TranCode.ExecuteUnitTest", elapsed)
    94  	}()
    95  
    96  	defer func() {
    97  		if err := recover(); err != nil {
    98  			iLog.Error(fmt.Sprintf("There is error to engine.TranCode.ExecuteUnitTest with error: %s", err))
    99  			//	f.ErrorMessage = fmt.Sprintf("There is error to engine.funcs.ThrowErrorFuncs.Execute with error: %s", err)
   100  
   101  		}
   102  	}()
   103  
   104  	tranobj, err := getTranCodeData(trancode, documents.DocDBCon)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	tf := NewTranFlow(tranobj, map[string]interface{}{}, systemsessions, nil, nil)
   109  	tf.TestwithSc = true
   110  
   111  	result, err := tf.UnitTest()
   112  
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	return result, nil
   117  }
   118  
   119  // ExecuteUnitTestWithTestData executes a unit test with the given test data for a specific trancode.
   120  // It takes the trancode string, testcase map, and systemsessions map as input parameters.
   121  // It returns a map[string]interface{} containing the test result and an error if any.
   122  // The test result contains the following fields:
   123  // - Name: the name of the unit test
   124  // - Inputs: the inputs of the unit test
   125  // - ExpectedOutputs: the expected outputs of the unit test
   126  // - ExpectError: a boolean value indicating whether the unit test is expected to return an error
   127  // - ExpectedError: the expected error message
   128  // - ActualOutputs: the actual outputs of the unit test
   129  // - ActualError: the actual error message
   130  // - Result: the result of the unit test, either "Pass" or "Fail"
   131  
   132  func ExecuteUnitTestWithTestData(trancode string, testcase map[string]interface{}, systemsessions map[string]interface{}) (map[string]interface{}, error) {
   133  	iLog := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TransCode"}
   134  	startTime := time.Now()
   135  	defer func() {
   136  		elapsed := time.Since(startTime)
   137  		iLog.PerformanceWithDuration("engine.TranCode.ExecuteUnitTestWithTestData", elapsed)
   138  	}()
   139  
   140  	defer func() {
   141  		if err := recover(); err != nil {
   142  			iLog.Error(fmt.Sprintf("There is error to engine.TranCode.ExecuteUnitTestWithTestData with error: %s", err))
   143  			//	f.ErrorMessage = fmt.Sprintf("There is error to engine.funcs.ThrowErrorFuncs.Execute with error: %s", err)
   144  
   145  		}
   146  	}()
   147  	tranobj, err := getTranCodeData(trancode, documents.DocDBCon)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	tf := NewTranFlow(tranobj, map[string]interface{}{}, systemsessions, nil, nil)
   152  	tf.TestwithSc = true
   153  
   154  	var testdata types.TestData
   155  
   156  	testdata.Inputs = testcase["inputs"].([]types.Input)
   157  	testdata.Outputs = testcase["outputs"].([]types.Output)
   158  	testdata.Name = testcase["name"].(string)
   159  	testdata.WantErr = testcase["wanterr"].(bool)
   160  	testdata.WantedErr = testcase["wantederr"].(string)
   161  
   162  	result, err := tf.UnitTestbyTestData(testdata)
   163  
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	return result, nil
   168  }
   169  
   170  // ExecutebyExternal executes a transaction code by calling an external service.
   171  // It takes a trancode string, a data map, a DBTx transaction, a DBCon database connection,
   172  // and a sc signalr client as input parameters.
   173  // It returns a map of outputs and an error.
   174  // The outputs map contains the outputs of the transaction code.
   175  // The error contains the error message if any.
   176  // The function also logs the performance of the transaction code execution.
   177  
   178  func ExecutebyExternal(trancode string, data map[string]interface{}, DBTx *sql.Tx, DBCon *documents.DocDB, sc signalr.Client) (map[string]interface{}, error) {
   179  	iLog := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TransCode"}
   180  	startTime := time.Now()
   181  	defer func() {
   182  		elapsed := time.Since(startTime)
   183  		iLog.PerformanceWithDuration("engine.TranCode.ExecutebyExternal", elapsed)
   184  	}()
   185  
   186  	defer func() {
   187  		if err := recover(); err != nil {
   188  			iLog.Error(fmt.Sprintf("There is error to engine.TranCode.ExecutebyExternal with error: %s", err))
   189  			//	f.ErrorMessage = fmt.Sprintf("There is error to engine.funcs.ThrowErrorFuncs.Execute with error: %s", err)
   190  
   191  		}
   192  	}()
   193  
   194  	tranobj, err := getTranCodeData(trancode, DBCon)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	tf := NewTranFlow(tranobj, data, map[string]interface{}{}, nil, nil, DBTx)
   199  	tf.DocDBCon = DBCon
   200  	tf.SignalRClient = sc
   201  
   202  	if callback_mgr.CallBackMap["TranCode_Execute"] == nil {
   203  		iLog.Debug("Register the trancode execution interface")
   204  		tfr := TranFlowstr{}
   205  		callback_mgr.RegisterCallBack("TranCode_Execute", tfr.Execute)
   206  
   207  	}
   208  
   209  	outputs, err := tf.Execute()
   210  
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	return outputs, nil
   215  }
   216  
   217  // NewTranFlow creates a new instance of TranFlow.
   218  // It takes the following parameters:
   219  // - tcode: the transaction code (types.TranCode)
   220  // - externalinputs: a map of external inputs (map[string]interface{})
   221  // - systemSession: a map of system session data (map[string]interface{})
   222  // - ctx: the context (context.Context)
   223  // - ctxcancel: the cancel function for the context (context.CancelFunc)
   224  // - dbTx: optional parameter for the database transaction (*sql.Tx)
   225  // It returns a pointer to TranFlow.
   226  
   227  func NewTranFlow(tcode types.TranCode, externalinputs, systemSession map[string]interface{}, ctx context.Context, ctxcancel context.CancelFunc, dbTx ...*sql.Tx) *TranFlow {
   228  	log := logger.Log{}
   229  	log.ModuleName = logger.TranCode
   230  	log.ControllerName = "Trancode"
   231  	if systemSession["UserNo"] != nil {
   232  		log.User = systemSession["UserNo"].(string)
   233  	} else {
   234  		log.User = "System"
   235  	}
   236  
   237  	startTime := time.Now()
   238  	defer func() {
   239  		elapsed := time.Since(startTime)
   240  		log.PerformanceWithDuration("engine.TranCode.ExecutebyExternal", elapsed)
   241  	}()
   242  
   243  	/*	defer func() {
   244  			if err := recover(); err != nil {
   245  				log.Error(fmt.Sprintf("There is error to engine.TranCode.ExecutebyExternal with error: %s", err))
   246  				//	f.ErrorMessage = fmt.Sprintf("There is error to engine.funcs.ThrowErrorFuncs.Execute with error: %s", err)
   247  
   248  			}
   249  		}()
   250  	*/
   251  	idbTx := append(dbTx, nil)[0]
   252  	if callback_mgr.CallBackMap["TranCode_Execute"] == nil {
   253  		log.Debug("Register the trancode execution interface")
   254  		tfr := TranFlowstr{}
   255  		callback_mgr.RegisterCallBack("TranCode_Execute", tfr.Execute)
   256  
   257  	}
   258  	/*
   259  		tfr := TranFlowstr{}
   260  		callback.RegisterCallBack("TranFlowstr_Execute", tfr.Execute)
   261  	*/
   262  	return &TranFlow{
   263  		Tcode:           tcode,
   264  		DBTx:            idbTx,
   265  		Ctx:             ctx,
   266  		CtxCancel:       ctxcancel,
   267  		ilog:            log,
   268  		Externalinputs:  externalinputs,
   269  		externaloutputs: map[string]interface{}{},
   270  		SystemSession:   systemSession,
   271  		DocDBCon:        documents.DocDBCon,
   272  		SignalRClient:   com.IACMessageBusClient,
   273  		ErrorMessage:    "",
   274  		TestwithSc:      false,
   275  		TestResults:     map[string]interface{}{},
   276  	}
   277  }
   278  
   279  // Execute executes the transaction flow.
   280  // It starts the timer to measure the execution time and logs the performance duration.
   281  // It recovers from any panics and logs the error message.
   282  // It retrieves the system session, external inputs, and external outputs from the transaction flow.
   283  // It starts a new database transaction if one doesn't exist.
   284  // It starts a new context with a timeout if one doesn't exist.
   285  // It executes the first function group of the transaction code and iterates through subsequent function groups until the code is no longer 1.
   286  // It commits the database transaction if it was started in this function.
   287  // It returns the external outputs and nil error if successful.
   288  func (t *TranFlow) Execute() (map[string]interface{}, error) {
   289  	startTime := time.Now()
   290  	defer func() {
   291  		elapsed := time.Since(startTime)
   292  		t.ilog.PerformanceWithDuration("engine.TranCode.Execute", elapsed)
   293  	}()
   294  	defer func() {
   295  		if r := recover(); r != nil {
   296  			t.ilog.Error(fmt.Sprintf("Error in Trancode.Execute: %s", r))
   297  			t.ErrorMessage = fmt.Sprintf("Error in Trancode.Execute: %s", r)
   298  			t.DBTx.Rollback()
   299  			t.CtxCancel()
   300  			return
   301  		}
   302  	}()
   303  
   304  	t.ilog.Info(fmt.Sprintf("Start process transaction code %s's %s ", t.Tcode.Name, "Execute"))
   305  	t.ilog.Debug(fmt.Sprintf("systemSession: %s", logger.ConvertJson(t.SystemSession)))
   306  	t.ilog.Debug(fmt.Sprintf("externalinputs: %s", logger.ConvertJson(t.Externalinputs)))
   307  	t.ilog.Debug(fmt.Sprintf("externaloutputs: %s", logger.ConvertJson(t.externaloutputs)))
   308  	systemSession := t.SystemSession
   309  	externalinputs := t.Externalinputs
   310  	externaloutputs := t.externaloutputs
   311  	userSession := map[string]interface{}{}
   312  	var err error
   313  	newTransaction := false
   314  
   315  	if t.DBTx == nil {
   316  
   317  		t.DBTx, err = dbconn.DB.Begin()
   318  		newTransaction = true
   319  		if err != nil {
   320  			t.ilog.Error(fmt.Sprintf("Error in Trancode.Execute during DB transaction beginning: %s", err.Error()))
   321  			return map[string]interface{}{}, err
   322  		}
   323  
   324  		defer t.DBTx.Rollback()
   325  	}
   326  
   327  	if t.Ctx == nil {
   328  		t.Ctx, t.CtxCancel = context.WithTimeout(context.Background(), time.Second*time.Duration(com.TransactionTimeout))
   329  
   330  		defer t.CtxCancel()
   331  	}
   332  
   333  	if t.TestwithSc {
   334  		t.TestResults = map[string]interface{}{}
   335  		t.TestResults["Name"] = t.Tcode.Name
   336  		t.TestResults["Version"] = t.Tcode.Version
   337  		t.TestResults["Inputs"] = t.Externalinputs
   338  		t.TestResults["SystemSession"] = t.SystemSession
   339  		t.TestResults["UserSession"] = userSession
   340  		t.TestResults["Outputs"] = t.externaloutputs
   341  		t.TestResults["FunctionGroups"] = []map[string]interface{}{}
   342  		t.TestResults["Error"] = t.ErrorMessage
   343  
   344  		tcom.SendTestResultMessageBus(t.Tcode.Name, "", "", "UnitTest", "Start",
   345  			t.Externalinputs, t.externaloutputs, t.SystemSession, map[string]interface{}{}, nil, t.SystemSession["ClientID"].(string), t.SystemSession["UserNo"].(string))
   346  	}
   347  
   348  	t.ilog.Debug(fmt.Sprintf("Start process transaction code %s's first func group: %s ", t.Tcode.Name, t.Tcode.Firstfuncgroup))
   349  	fgroup, code := t.getFGbyName(t.Tcode.Firstfuncgroup)
   350  	t.ilog.Debug(fmt.Sprintf("start first function group:", logger.ConvertJson(fgroup)))
   351  
   352  	for code == 1 {
   353  		fg := funcgroup.NewFGroup(t.DocDBCon, t.SignalRClient, t.DBTx, fgroup, "", systemSession, userSession, externalinputs, externaloutputs, t.Ctx, t.CtxCancel)
   354  
   355  		fg.TestwithSc = t.TestwithSc
   356  
   357  		fg.Execute()
   358  
   359  		if t.TestwithSc {
   360  			t.TestResults["FunctionGroups"] = append(t.TestResults["FunctionGroups"].([]map[string]interface{}), fg.TestResults)
   361  		}
   362  
   363  		externalinputs = fg.Externalinputs
   364  		externaloutputs = fg.Externaloutputs
   365  		userSession = fg.UserSession
   366  
   367  		if fg.Nextfuncgroup == "" {
   368  			code = 0
   369  			break
   370  		} else {
   371  			fgroup, code = t.getFGbyName(fg.Nextfuncgroup)
   372  			t.ilog.Debug(fmt.Sprintf("function group:%s, Code:%d", logger.ConvertJson(fgroup), code))
   373  		}
   374  	}
   375  
   376  	if newTransaction {
   377  		err := t.DBTx.Commit()
   378  		if err != nil {
   379  			t.ilog.Error(fmt.Sprintf("Error in Trancode.Execute during DB transaction commit: %s", err.Error()))
   380  			t.CtxCancel()
   381  			return map[string]interface{}{}, err
   382  		}
   383  	}
   384  
   385  	if t.TestwithSc {
   386  		t.TestResults["Outputs"] = externaloutputs
   387  		t.TestResults["Error"] = t.ErrorMessage
   388  	}
   389  	return externaloutputs, nil
   390  
   391  }
   392  
   393  // getFGbyName retrieves the FuncGroup by its name from the TranFlow.
   394  // It returns the found FuncGroup and a flag indicating whether the FuncGroup was found or not.
   395  
   396  func (t *TranFlow) getFGbyName(name string) (types.FuncGroup, int) {
   397  	startTime := time.Now()
   398  	defer func() {
   399  		elapsed := time.Since(startTime)
   400  		t.ilog.PerformanceWithDuration("engine.TranCode.getFGbyName", elapsed)
   401  	}()
   402  	/*	defer func() {
   403  			if r := recover(); r != nil {
   404  				t.ilog.Error(fmt.Sprintf("Error in Trancode.getFGbyName: %s", r))
   405  				t.ErrorMessage = fmt.Sprintf("Error in Trancode.getFGbyName: %s", r)
   406  				t.DBTx.Rollback()
   407  				t.CtxCancel()
   408  				return
   409  			}
   410  		}()
   411  	*/
   412  	t.ilog.Debug(fmt.Sprintf("Get the Func group by name: %s", name))
   413  	for _, fgroup := range t.Tcode.Functiongroups {
   414  		if fgroup.Name == name {
   415  
   416  			return fgroup, 1
   417  		}
   418  	}
   419  	t.ilog.Debug(fmt.Sprintf("Can't find the Func group by name: %s", name))
   420  	return types.FuncGroup{}, 0
   421  }
   422  
   423  // GetTransCode retrieves the transaction code for the given name.
   424  // It reads the transaction code configuration file and returns the corresponding TranCode object.
   425  // If an error occurs during the process, it returns an empty TranCode object and an error.
   426  func GetTransCode(name string) (types.TranCode, error) {
   427  	log := logger.Log{}
   428  	log.ModuleName = logger.TranCode
   429  	log.ControllerName = "Trancode"
   430  	log.User = "System"
   431  
   432  	startTime := time.Now()
   433  	defer func() {
   434  		elapsed := time.Since(startTime)
   435  		log.PerformanceWithDuration("engine.TranCode.GetTranCode", elapsed)
   436  	}()
   437  	defer func() {
   438  		if r := recover(); r != nil {
   439  			log.Error(fmt.Sprintf("Error in Trancode.GetTranCode: %s", r))
   440  			return
   441  		}
   442  	}()
   443  
   444  	log.Info(fmt.Sprintf("Start get transaction code %s", name))
   445  
   446  	log.Info(fmt.Sprintf("./%s/%s%s", "trancodes", name, ".json"))
   447  	data, err := ioutil.ReadFile(fmt.Sprintf("./%s/%s%s", "trancodes", name, ".json"))
   448  	if err != nil {
   449  		log.Error(fmt.Sprintf("failed to read configuration file: %v", err))
   450  		return types.TranCode{}, fmt.Errorf("failed to read configuration file: %v", err)
   451  	}
   452  	log.Debug(fmt.Sprintf("Read the tran code configuration:%s", string(data)))
   453  	//	fmt.Println(string(data))
   454  	return Bytetoobj(data)
   455  }
   456  
   457  // Bytetoobj converts a byte slice to a TranCode object.
   458  // It parses the transaction code configuration from the provided byte slice and returns a TranCode object.
   459  // If there is an error during parsing, it returns an empty TranCode object and an error.
   460  
   461  func Bytetoobj(config []byte) (types.TranCode, error) {
   462  	log := logger.Log{}
   463  	log.ModuleName = logger.TranCode
   464  	log.ControllerName = "Trancode"
   465  	log.User = "System"
   466  	startTime := time.Now()
   467  	defer func() {
   468  		elapsed := time.Since(startTime)
   469  		log.PerformanceWithDuration("engine.TranCode.Bytetoobj", elapsed)
   470  	}()
   471  	/*	defer func() {
   472  			if r := recover(); r != nil {
   473  				log.Error(fmt.Sprintf("Error in Trancode.Byetoobj: %s", r))
   474  				return
   475  			}
   476  		}()
   477  	*/
   478  	log.Info(fmt.Sprintf("Start parse transaction code configuration"))
   479  
   480  	var tranCode types.TranCode
   481  	if err := json.Unmarshal(config, &tranCode); err != nil {
   482  		return types.TranCode{}, fmt.Errorf("failed to parse configuration file: %v", err)
   483  	}
   484  	log.Debug(fmt.Sprintf("Parse the tran code configuration:%s", logger.ConvertJson(tranCode)))
   485  	return tranCode, nil
   486  }
   487  
   488  func Configtoobj(config string) (types.TranCode, error) {
   489  
   490  	return Bytetoobj([]byte(config))
   491  }
   492  
   493  type TranCodeData struct {
   494  	TranCode string                 `json:"code"`
   495  	inputs   map[string]interface{} `json:"Inputs"`
   496  }
   497  
   498  type TranFlowstr struct {
   499  	TestwithSc bool `json:"TestwithSc,omitempty"`
   500  }
   501  
   502  func (t *TranFlowstr) Execute(tcode string, inputs map[string]interface{}, sc signalr.Client, docdbconn *documents.DocDB, ctx context.Context, ctxcancel context.CancelFunc, dbTx ...*sql.Tx) (map[string]interface{}, error) {
   503  	log := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TransCode.TranFlow"}
   504  	startTime := time.Now()
   505  	defer func() {
   506  		elapsed := time.Since(startTime)
   507  		log.PerformanceWithDuration("engine.TranCode.Tranflow.Execute", elapsed)
   508  	}()
   509  	defer func() {
   510  		if r := recover(); r != nil {
   511  			log.Error(fmt.Sprintf("Error in Trancode.TranFLow.Execute: %s", r))
   512  			return
   513  		}
   514  	}()
   515  
   516  	tc, err := GetTranCodeDatabyCode(tcode)
   517  
   518  	if err != nil {
   519  		return nil, err
   520  	}
   521  	systemSession := map[string]interface{}{}
   522  	externalinputs := inputs
   523  
   524  	idbTx := append(dbTx, nil)[0]
   525  
   526  	tf := NewTranFlow(tc, externalinputs, systemSession, ctx, ctxcancel, idbTx)
   527  	tf.SignalRClient = sc
   528  	tf.TestwithSc = t.TestwithSc
   529  
   530  	tf.DocDBCon = docdbconn
   531  
   532  	return tf.Execute()
   533  }
   534  
   535  func (t *TranFlow) UnitTestbyTestData(testdata types.TestData) (map[string]interface{}, error) {
   536  
   537  	startTime := time.Now()
   538  	defer func() {
   539  		elapsed := time.Since(startTime)
   540  		t.ilog.PerformanceWithDuration("engine.TranCode.Tranflow.UnitTestbyTestData", elapsed)
   541  	}()
   542  
   543  	defer func() {
   544  		if r := recover(); r != nil {
   545  			t.ilog.Error(fmt.Sprintf("Error in Trancode.UnitTestbyTestData: %s", r))
   546  			t.ErrorMessage = fmt.Sprintf("Error in Trancode.UnitTestbyTestData: %s", r)
   547  			t.DBTx.Rollback()
   548  			t.CtxCancel()
   549  			return
   550  		}
   551  	}()
   552  
   553  	t.ilog.Debug(fmt.Sprintf("Start process transaction code %s's with test data: %s ", t.Tcode.Name, testdata))
   554  	t.ilog.Debug(fmt.Sprintf("externalinputs: %s", logger.ConvertJson(testdata.Inputs)))
   555  	t.ilog.Debug(fmt.Sprintf("expected externaloutputs: %s", logger.ConvertJson(testdata.Outputs)))
   556  
   557  	t.Externalinputs = convertInputsToMap(testdata.Inputs)
   558  	t.externaloutputs = map[string]interface{}{}
   559  	testresult := map[string]interface{}{}
   560  	testresult["Name"] = testdata.Name
   561  	testresult["Inputs"] = testdata.Inputs
   562  	testresult["ExpectedOutputs"] = testdata.Outputs
   563  	testresult["ExpectError"] = testdata.WantErr
   564  	testresult["ExpectedError"] = testdata.WantedErr
   565  
   566  	tcom.SendTestResultMessageBus(t.Tcode.Name, "", "", "UnitTest", "Start",
   567  		t.Externalinputs, t.externaloutputs, t.SystemSession, map[string]interface{}{}, nil, t.SystemSession["ClientID"].(string), t.SystemSession["UserNo"].(string))
   568  
   569  	outputs, err := t.Execute()
   570  
   571  	tcom.SendTestResultMessageBus(t.Tcode.Name, "", "", "UnitTest", "Complete",
   572  		t.Externalinputs, outputs, t.SystemSession, map[string]interface{}{}, err, t.SystemSession["ClientID"].(string), t.SystemSession["UserNo"].(string))
   573  
   574  	t.ilog.Debug(fmt.Sprintf("actual externaloutputs: %v, expected outputs: %v", outputs, testdata.Outputs))
   575  	if err != nil {
   576  		t.ilog.Error(fmt.Sprintf("Error in Trancode.Execute: %s", err.Error()))
   577  
   578  		if testdata.WantErr {
   579  			if testdata.WantedErr == err.Error() {
   580  				testresult["ActualError"] = err.Error()
   581  				testresult["Result"] = "Pass"
   582  
   583  			} else {
   584  				testresult["ActualError"] = err.Error()
   585  				testresult["Result"] = "Pass"
   586  
   587  			}
   588  
   589  		} else {
   590  			testresult["ActualError"] = err.Error()
   591  			testresult["Result"] = "Fail"
   592  
   593  		}
   594  	}
   595  
   596  	if !testdata.WantErr {
   597  		if !compareMap(outputs, convertOutputsToMap(testdata.Outputs)) {
   598  			testresult["ActualOutputs"] = outputs
   599  			testresult["Result"] = "Fail"
   600  
   601  		} else {
   602  			testresult["ActualOutputs"] = outputs
   603  			testresult["Result"] = "Pass"
   604  
   605  		}
   606  	} else {
   607  		testresult["Result"] = "Fail"
   608  		testresult["ActualOutputs"] = outputs
   609  		testresult["ActualError"] = ""
   610  
   611  	}
   612  
   613  	return testresult, nil
   614  }
   615  
   616  func (t *TranFlow) UnitTest() (map[string]interface{}, error) {
   617  	startTime := time.Now()
   618  	defer func() {
   619  		elapsed := time.Since(startTime)
   620  		t.ilog.PerformanceWithDuration("engine.TranCode.Tranflow.UnitTest", elapsed)
   621  	}()
   622  
   623  	defer func() {
   624  		if r := recover(); r != nil {
   625  			t.ilog.Error(fmt.Sprintf("Error in Trancode.UnitTest: %s", r))
   626  			t.ErrorMessage = fmt.Sprintf("Error in Trancode.UnitTest: %s", r)
   627  			t.DBTx.Rollback()
   628  			t.CtxCancel()
   629  			return
   630  		}
   631  	}()
   632  
   633  	result := make(map[string]interface{})
   634  
   635  	t.ilog.Info(fmt.Sprintf("Start Process for transaction code %s's %s ", t.Tcode.Name, "Unit Test"))
   636  	t.ilog.Debug(fmt.Sprintf("systemSession: %s", logger.ConvertJson(t.SystemSession)))
   637  	testdatalist := t.Tcode.TestDatas
   638  
   639  	for _, testdata := range testdatalist {
   640  
   641  		testresult, _ := t.UnitTestbyTestData(testdata)
   642  		result[testdata.Name] = testresult
   643  
   644  	}
   645  
   646  	return result, nil
   647  }
   648  
   649  func GetTranCodeDatabyCode(Code string) (types.TranCode, error) {
   650  	log := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TransCode"}
   651  	startTime := time.Now()
   652  	defer func() {
   653  		elapsed := time.Since(startTime)
   654  		log.PerformanceWithDuration("engine.TranCode.GetTranCodeDatabyCode", elapsed)
   655  	}()
   656  	defer func() {
   657  		if r := recover(); r != nil {
   658  			log.Error(fmt.Sprintf("Error in Trancode.GetTranCodeDatabyCode: %s", r))
   659  			return
   660  		}
   661  	}()
   662  
   663  	trancodeobj, err := getTranCodeData(Code, documents.DocDBCon)
   664  	if err != nil {
   665  		return types.TranCode{}, err
   666  	}
   667  	return trancodeobj, nil
   668  }
   669  
   670  func getTranCodeData(Code string, DBConn *documents.DocDB) (types.TranCode, error) {
   671  	iLog := logger.Log{ModuleName: logger.API, User: "System", ControllerName: "TranCode"}
   672  
   673  	startTime := time.Now()
   674  	defer func() {
   675  		elapsed := time.Since(startTime)
   676  		iLog.PerformanceWithDuration("engine.TranCode.getTranCodeData", elapsed)
   677  	}()
   678  	defer func() {
   679  		if r := recover(); r != nil {
   680  			iLog.Error(fmt.Sprintf("Error in Trancode.getTranCodeData: %s", r))
   681  			return
   682  		}
   683  	}()
   684  
   685  	iLog.Info(fmt.Sprintf("Get the trancode code for %s ", Code))
   686  
   687  	iLog.Info(fmt.Sprintf("Start process transaction code %s's %s ", Code, "Execute"))
   688  
   689  	filter := bson.M{"trancodename": Code, "isdefault": true}
   690  
   691  	tcode, err := DBConn.QueryCollection("Transaction_Code", filter, nil)
   692  
   693  	if err != nil {
   694  		iLog.Error(fmt.Sprintf("Get transaction code %s's error", Code))
   695  
   696  		return types.TranCode{}, err
   697  	}
   698  	iLog.Debug(fmt.Sprintf("transaction code %s's data: %s", Code, tcode))
   699  	jsonString, err := json.Marshal(tcode[0])
   700  	if err != nil {
   701  
   702  		iLog.Error(fmt.Sprintf("Error marshaling json:", err.Error()))
   703  		return types.TranCode{}, err
   704  	}
   705  
   706  	trancodeobj, err := Configtoobj(string(jsonString))
   707  	if err != nil {
   708  		iLog.Error(fmt.Sprintf("Error unmarshaling json:", err.Error()))
   709  		return types.TranCode{}, err
   710  	}
   711  
   712  	iLog.Debug(fmt.Sprintf("transaction code %s's json: %s", trancodeobj, string(jsonString)))
   713  
   714  	if err != nil {
   715  		iLog.Error(fmt.Sprintf("Error unmarshaling json:", err.Error()))
   716  		return types.TranCode{}, err
   717  	}
   718  
   719  	return trancodeobj, nil
   720  }
   721  
   722  func convertInputsToMap(inputs []types.Input) map[string]interface{} {
   723  	iLog := logger.Log{ModuleName: logger.API, User: "System", ControllerName: "TranCode"}
   724  
   725  	startTime := time.Now()
   726  	defer func() {
   727  		elapsed := time.Since(startTime)
   728  		iLog.PerformanceWithDuration("engine.TranCode.convertInputsToMap", elapsed)
   729  	}()
   730  	defer func() {
   731  		if r := recover(); r != nil {
   732  			iLog.Error(fmt.Sprintf("Error in Trancode.convertInputsToMap: %s", r))
   733  			return
   734  		}
   735  	}()
   736  
   737  	result := map[string]interface{}{}
   738  
   739  	for _, input := range inputs {
   740  		result[input.Name] = input.Value
   741  	}
   742  
   743  	return result
   744  }
   745  
   746  func convertOutputsToMap(outputs []types.Output) map[string]interface{} {
   747  	iLog := logger.Log{ModuleName: logger.API, User: "System", ControllerName: "TranCode"}
   748  
   749  	startTime := time.Now()
   750  	defer func() {
   751  		elapsed := time.Since(startTime)
   752  		iLog.PerformanceWithDuration("engine.TranCode.convertOutputsToMap", elapsed)
   753  	}()
   754  	defer func() {
   755  		if r := recover(); r != nil {
   756  			iLog.Error(fmt.Sprintf("Error in Trancode.convertOutputsToMap: %s", r))
   757  			return
   758  		}
   759  	}()
   760  
   761  	result := map[string]interface{}{}
   762  
   763  	for _, output := range outputs {
   764  		result[output.Name] = output.Value
   765  	}
   766  
   767  	return result
   768  }
   769  
   770  func compareMap(map1, map2 map[string]interface{}) bool {
   771  	iLog := logger.Log{ModuleName: logger.TranCode, User: "System", ControllerName: "TranCode"}
   772  
   773  	startTime := time.Now()
   774  	defer func() {
   775  		elapsed := time.Since(startTime)
   776  		iLog.PerformanceWithDuration("engine.TranCode.compareMap", elapsed)
   777  	}()
   778  	defer func() {
   779  		if r := recover(); r != nil {
   780  			iLog.Error(fmt.Sprintf("Error in Trancode.compareMap: %s", r))
   781  			return
   782  		}
   783  	}()
   784  
   785  	if len(map1) != len(map2) {
   786  		return false
   787  	}
   788  
   789  	for key1, value1 := range map1 {
   790  		value2, ok := map2[key1]
   791  		if !ok {
   792  			return false
   793  		}
   794  
   795  		if value1 != value2 {
   796  			return false
   797  		}
   798  	}
   799  
   800  	return true
   801  }