Newer
Older
//@author Ajit Jagtap
//@version Mon Jul 09 2018 14:00:05 GMT+0530 (IST)
// Package servicebuildermdl will help you run BL and fetch data.
"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/constantmdl"
"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 globalConfig map[string]string
var once sync.Once
globalConfig = make(map[string]string)
// 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{}
pricipalObject Principal
globalConfigData map[string]string
}
// SetGlobalConfig - SetGlobalConfig
func SetGlobalConfig(configs map[string]string) {
once.Do(func() {
if configs != nil {
globalConfig = configs
}
})
}
// 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) {
temp, found := ab.localServiceData[key]
if errormdl.CheckBool(!found) {
return 0, false
}
// cast it
if errormdl.CheckBool1(!ok) {
return 0, false
}
return value, true
}
// GetDataInterface will give you int
func (ab *AbstractBusinessLogicHolder) GetDataInterface(key string) (interface{}, bool) {
//check in map
temp, found := ab.localServiceData[key]
if errormdl.CheckBool(!found) {
return nil, false
}
return temp, true
}
// GetDataResultset will give you int
func (ab *AbstractBusinessLogicHolder) GetDataResultset(key string) (*gjson.Result, bool) {
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
}
func (ab *AbstractBusinessLogicHolder) GetMQLRequestData() (*gjson.Result, bool) {
//check in map
temp, found := ab.localServiceData[constantmdl.MQLRequestData]
if errormdl.CheckBool(!found) {
loggermdl.LogWarn("MQL Request Data not Found")
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) {
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
}
// GetCustomData will give you string
func (ab *AbstractBusinessLogicHolder) GetCustomData(key string) (interface{}, bool) {
temp, found := ab.localServiceData[key]
if errormdl.CheckBool(!found) {
return 0, false
}
// cast it
return temp, true
}
// GetGlobalConfigString - return string value for global config key
func (ab *AbstractBusinessLogicHolder) GetGlobalConfigString(key string) (string, bool) {
value, found := ab.globalConfigData[key]
if errormdl.CheckBool(!found) {
return "", false
}
return value, true
}
func (ab *AbstractBusinessLogicHolder) New(principalObject *Principal) *AbstractBusinessLogicHolder {
ab.localServiceData = make(map[string]interface{})
ab.globalConfigData = globalConfig
ab.pricipalObject = *principalObject
// SetResultset will return map data with finaldata key
func (ab *AbstractBusinessLogicHolder) SetResultset(key string, obj *gjson.Result) {
ab.localServiceData[key] = obj
}
// SetMQLRequestData - set value
func (ab *AbstractBusinessLogicHolder) SetMQLRequestData(obj *gjson.Result) {
ab.localServiceData[constantmdl.MQLRequestData] = 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"]
// SetFinalData will return map data with finaldata key
func (ab *AbstractBusinessLogicHolder) SetFinalData(data interface{}) {
ab.localServiceData["finaldata"] = data
}
// EchoBL sample EchoBL logic handler
func (ab *AbstractBusinessLogicHolder) EchoBL() (map[string]interface{}, error) {
return map[string]interface{}{
"ok": int64(1),
}
// 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
}
// 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.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)
step.RunFunc = blfunc
step.ErrorFunc = errorfunc
step.Stepname = stepname
step.processDataFunc = ld
sb.steps = append(sb.steps, step)
return sb
}
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
// AddStepWithGoTo - AddStep with goto pointer
func (sb *ServiceBuilder) AddStepWithGoTo(stepname, rule string, ld LoadData, blfunc, errorfunc func() (map[string]interface{}, error), gotoStepName string) *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
mutex.Unlock()
}
step.RunFunc = blfunc
step.ErrorFunc = errorfunc
step.Stepname = stepname
step.processDataFunc = ld
step.JumpStep = gotoStepName
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
}
func (sb *ServiceBuilder) findStepIndex(stepName string) (int, bool) {
for i, step := range sb.steps {
if step.Stepname == stepName {
return i, true
}
}
return 0, false
}
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 i := 0; i < len(sb.steps); i++ {
if maxStepCount == 0 {
loggermdl.LogError("Your steps are in recursion and cross limit of 100 steps")
return nil, errormdl.Wrap("Your steps are in recursion and cross limit of 100 steps")
if sb.steps[i].IsValidator {
validationError := sb.executeValidationFunction(sb.steps[i].ValidationDataKey, sb.steps[i].ValidationFunc)
if errormdl.CheckErr(validationError) != nil {
return nil, errormdl.CheckErr(validationError)
}
continue
}
if sb.steps[i].processDataFunc != nil {
daoError := sb.steps[i].processDataFunc(sb.businessLogicHolder)
if errormdl.CheckErr1(daoError) != nil {
loggermdl.LogError(daoError)
tmp, blError := sb.steps[i].RunFunc()
if errormdl.CheckErr2(blError) != nil {
loggermdl.LogError(blError)
return nil, errormdl.CheckErr2(blError)
}
// Validation using conditions
result, evaluteError := conditions.Evaluate(sb.steps[i].expr, tmp)
if errormdl.CheckErr3(evaluteError) != nil {
loggermdl.LogError(evaluteError)
return nil, errormdl.CheckErr3(evaluteError)
}
// if validation fails
// loggermdl.LogWarn(sb.steps[i].Stepname, "Failed", result)
// jump step is a functionality like go to on particular step
if sb.steps[i].JumpStep != "" {
_, recoveryError := sb.executeErrorFunction(sb.steps[i].ErrorFunc)
if errormdl.CheckErr(recoveryError) != nil {
loggermdl.LogError(recoveryError)
return nil, errormdl.CheckErr(recoveryError)
}
index, ok := sb.findStepIndex(sb.steps[i].JumpStep)
if !ok {
loggermdl.LogError("Step Name spcify in GOTO not found: " + sb.steps[i].JumpStep)
return nil, errormdl.Wrap("Step Name spcify in GOTO not found: " + sb.steps[i].JumpStep)
}
i = index - 1
continue
return sb.executeErrorFunction(sb.steps[i].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 {
loggermdl.LogError("Data Not Found For Validation: " + dataKey)
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) {
loggermdl.LogError("Data Validation failed and No recovery function found")
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) {
return sb.businessLogicHolder.GetFinalData(), nil
}
return fn(sb.businessLogicHolder)
}
// Principal - Object inside JWT token
type Principal struct {
UserID string `json:"userId"`
Groups []string `json:"groups"`
SessionExpiration time.Time `json:"sessionExpiration"`
HitsCount int `json:"hitsCount"`
}
// // SetPrincipalObject - Set Principal object to BLHolder
// func (ab *AbstractBusinessLogicHolder) SetPrincipalObject(object *Principal) {
// ab.pricipalObject = *object
// }
// GetPrincipalObject - return Principal object from BLHolder
func (ab *AbstractBusinessLogicHolder) GetPrincipalObject() *Principal {
return &ab.pricipalObject
}