servicebuildermdl.go 8.87 KiB
//@author  Ajit Jagtap
//@version Mon Jul 09 2018 14:00:05 GMT+0530 (IST)
// Package servicebuildermdl will help you run BL and fetch data.
package servicebuildermdl
import (
	"strings"
	"sync"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/validationmdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/errormdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
	"github.com/oleksandr/conditions"
	"github.com/tidwall/gjson"
var ruleCache map[string]conditions.Expr
var mutex = &sync.Mutex{}
func init() {
	ruleCache = make(map[string]conditions.Expr)
// LoadData is a method sign for loader methods
type LoadData = func(ab *AbstractBusinessLogicHolder) error
// FinalStepProcessOutput is a method sign for loader methods
type FinalStepProcessOutput = func(ab *AbstractBusinessLogicHolder) (*interface{}, error)
// AbstractBusinessLogicHolder use this type to inheritance
type AbstractBusinessLogicHolder struct {
	localServiceData map[string]interface{}
// GetDataString will give you string
func (ab *AbstractBusinessLogicHolder) GetDataString(key string) (string, bool) {
	//check in map
	temp, found := ab.localServiceData[key]
	if errormdl.CheckBool(!found) {
		return "", false
	// cast it
	value, ok := temp.(string)
	if errormdl.CheckBool1(!ok) {
		return "", false
	return value, true
// GetDataInt will give you int
func (ab *AbstractBusinessLogicHolder) GetDataInt(key string) (int64, bool) {
	//check in map
	temp, found := ab.localServiceData[key]
	if errormdl.CheckBool(!found) {
		return 0, false
	// cast it
	value, ok := temp.(int64)
	if errormdl.CheckBool1(!ok) {
		return 0, false
	return value, true
// GetDataResultset will give you int
func (ab *AbstractBusinessLogicHolder) GetDataResultset(key string) (*gjson.Result, bool) {
	//check in map
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
temp, found := ab.localServiceData[key] if errormdl.CheckBool(!found) { loggermdl.LogWarn("Key not found -", key) return &gjson.Result{}, false } // cast it value, ok := temp.(*gjson.Result) if errormdl.CheckBool1(!ok) { return &gjson.Result{}, false } return value, true } // GetDataBool will give you int func (ab *AbstractBusinessLogicHolder) GetDataBool(key string) (bool, bool) { //check in map temp, found := ab.localServiceData[key] if errormdl.CheckBool(!found) { return false, false } // cast it value, ok := temp.(bool) if errormdl.CheckBool1(!ok) { return false, false } return value, true } // GetDataInterface will give you string func (ab *AbstractBusinessLogicHolder) GetDataInterface(key string) (interface{}, bool) { //check in map temp, found := ab.localServiceData[key] if errormdl.CheckBool(!found) { return 0, false } // cast it return temp, true } // New will create memory for your data func (ab *AbstractBusinessLogicHolder) New() *AbstractBusinessLogicHolder { ab.localServiceData = make(map[string]interface{}) return ab } // SetResultset will return map data with finaldata key func (ab *AbstractBusinessLogicHolder) SetResultset(key string, obj *gjson.Result) { ab.localServiceData[key] = obj } // SetByteData will set byte data as gjson.Result func (ab *AbstractBusinessLogicHolder) SetByteData(key string, obj []byte) { rs := gjson.ParseBytes(obj) ab.localServiceData[key] = rs } // SetCustomData set custom user data in map func (ab *AbstractBusinessLogicHolder) SetCustomData(key string, data interface{}) { ab.localServiceData[key] = data } // GetFinalData will return map data with finaldata key func (ab *AbstractBusinessLogicHolder) GetFinalData() *interface{} { a := ab.localServiceData["finaldata"] return &a } // SetFinalData will return map data with finaldata key func (ab *AbstractBusinessLogicHolder) SetFinalData(data interface{}) {
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
ab.localServiceData["finaldata"] = data } // EchoBL sample EchoBL logic handler func (ab *AbstractBusinessLogicHolder) EchoBL() (map[string]interface{}, error) { // loggermdl.LogWarn("EchoBL called") return map[string]interface{}{ "ok": int64(1), }, nil } // Step help to maintain steps type Step struct { Stepname string expr conditions.Expr processDataFunc LoadData IsValidator bool ValidationDataKey string ValidationFunc func(interface{}) error RunFunc func() (map[string]interface{}, error) ErrorFunc func() (map[string]interface{}, error) } // ServiceBuilder will help you to run steps type ServiceBuilder struct { ServiceName string steps []Step businessLogicHolder *AbstractBusinessLogicHolder ServiceError error } // GetSB Gives you service builder from where you can run steps func GetSB(name string, ab *AbstractBusinessLogicHolder) *ServiceBuilder { newsb := ServiceBuilder{} newsb.ServiceName = name newsb.businessLogicHolder = ab return &newsb } // AddStep will add func step with rule // Stepname : Give Name to step. It will appear in log. // Rule : Give Ybl rule // blfunc : Give Business Logic function pointer // errorfunc : Give Error function pointer func (sb *ServiceBuilder) AddStep(stepname, rule string, ld LoadData, blfunc, errorfunc func() (map[string]interface{}, error)) *ServiceBuilder { if errormdl.CheckErr(sb.ServiceError) != nil { loggermdl.LogError(sb.ServiceError) return sb } step := Step{} //Check rule in cache mutex.Lock() cachedRule, found := ruleCache[rule] mutex.Unlock() if errormdl.CheckBool(found) { step.expr = cachedRule } else { // Parse the condition language and get expression p := conditions.NewParser(strings.NewReader(rule)) expr, err := p.Parse() if errormdl.CheckErr1(err) != nil { loggermdl.LogError("Error in step: ", stepname, err) sb.ServiceError = errormdl.CheckErr1(err) return sb } step.expr = expr mutex.Lock() ruleCache[rule] = expr
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
mutex.Unlock() } step.RunFunc = blfunc step.ErrorFunc = errorfunc step.Stepname = stepname step.processDataFunc = ld sb.steps = append(sb.steps, step) return sb } // AddValidation add validation step for give dataKey func (sb *ServiceBuilder) AddValidation(dataKey string, validationfunc func(interface{}) error) *ServiceBuilder { if errormdl.CheckErr(sb.ServiceError) != nil { loggermdl.LogError(sb.ServiceError) return sb } step := Step{} step.IsValidator = true step.ValidationDataKey = dataKey step.ValidationFunc = validationfunc step.Stepname = "Validation Step" sb.steps = append(sb.steps, step) return sb } // Run all Steps one by one func (sb *ServiceBuilder) Run(fn FinalStepProcessOutput) (*interface{}, error) { if errormdl.CheckErr(sb.ServiceError) != nil { loggermdl.LogError(sb.ServiceError) return nil, errormdl.CheckErr(sb.ServiceError) } _, ok := sb.businessLogicHolder.localServiceData["finaldata"] if ok { return sb.businessLogicHolder.GetFinalData(), nil } for _, step := range sb.steps { // Validation if step.IsValidator { validationError := sb.executeValidationFunction(step.ValidationDataKey, step.ValidationFunc) if errormdl.CheckErr(validationError) != nil { return nil, errormdl.CheckErr(validationError) } continue } //Load Data if step.processDataFunc != nil { daoError := step.processDataFunc(sb.businessLogicHolder) if errormdl.CheckErr1(daoError) != nil { loggermdl.LogError(daoError) return nil, errormdl.CheckErr1(daoError) } } //Run step func tmp, blError := step.RunFunc() if errormdl.CheckErr2(blError) != nil { loggermdl.LogError(blError) return nil, errormdl.CheckErr2(blError) } // Validation using conditions result, evaluteError := conditions.Evaluate(step.expr, tmp) if errormdl.CheckErr3(evaluteError) != nil { loggermdl.LogError(evaluteError) return nil, errormdl.CheckErr3(evaluteError) } // if validation fails if !result { // loggermdl.LogWarn(step.Stepname, "Failed", result)
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
return sb.executeErrorFunction(step.ErrorFunc) } } return sb.finalOutput(fn) } // executeValidationFunction exceute when validation failed for any step func (sb *ServiceBuilder) executeValidationFunction(dataKey string, fn func(interface{}) error) error { validationData, ok := sb.businessLogicHolder.localServiceData[dataKey] if !ok { return errormdl.Wrap("Data Not Found For Validation: " + dataKey) } if fn == nil { return validationmdl.ValidateStruct(validationData) } return fn(validationData) } // executeErrorFunction exceute when validation failed for any step func (sb *ServiceBuilder) executeErrorFunction(fn func() (map[string]interface{}, error)) (*interface{}, error) { if fn == nil { return nil, errormdl.Wrap("Data Validation failed and No recovery function found") } _, err := fn() if errormdl.CheckErr(err) != nil { loggermdl.LogError(err) return nil, errormdl.CheckErr(err) } return sb.businessLogicHolder.GetFinalData(), nil } // finalOutput return Final output func (sb *ServiceBuilder) finalOutput(fn FinalStepProcessOutput) (*interface{}, error) { if fn == nil { return sb.businessLogicHolder.GetFinalData(), nil } return fn(sb.businessLogicHolder) }