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/validationmdl"
"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/errormdl"
"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
"github.com/oleksandr/conditions"
"github.com/tidwall/gjson"
// 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
}
// 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
}
// 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
}
// 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
}
func (ab *AbstractBusinessLogicHolder) New(principalObject *Principal) *AbstractBusinessLogicHolder {
ab.localServiceData = make(map[string]interface{})
ab.pricipalObject = *principalObject
// 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"]
// 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
}
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
// 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
}