resource/processor.go

Summary

Maintainability
B
4 hrs
Test Coverage
package resource

import (
    "database/sql"
    "errors"
    "reflect"

    "github.com/jinzhu/gorm"
    "github.com/qor/qor"
    "github.com/qor/qor/utils"
    "github.com/qor/roles"
)

// ErrProcessorSkipLeft skip left processors error, if returned this error in validation, before callbacks, then qor will stop process following processors
var ErrProcessorSkipLeft = errors.New("resource: skip left")

type processor struct {
    Result     interface{}
    Resource   Resourcer
    Context    *qor.Context
    MetaValues *MetaValues
    SkipLeft   bool
}

// DecodeToResource decode meta values to resource result
func DecodeToResource(res Resourcer, result interface{}, metaValues *MetaValues, context *qor.Context) *processor {
    return &processor{Resource: res, Result: result, Context: context, MetaValues: metaValues}
}

func (processor *processor) checkSkipLeft(errs ...error) bool {
    if processor.SkipLeft {
        return true
    }

    for _, err := range errs {
        if err == ErrProcessorSkipLeft {
            processor.SkipLeft = true
            break
        }
    }
    return processor.SkipLeft
}

// Initialize initialize a processor
func (processor *processor) Initialize() error {
    err := processor.Resource.CallFindOne(processor.Result, processor.MetaValues, processor.Context)
    processor.checkSkipLeft(err)
    return err
}

// Validate run validators
func (processor *processor) Validate() error {
    var errs qor.Errors
    if processor.checkSkipLeft() {
        return nil
    }

    for _, v := range processor.Resource.GetResource().Validators {
        if errs.AddError(v.Handler(processor.Result, processor.MetaValues, processor.Context)); !errs.HasError() {
            if processor.checkSkipLeft(errs.GetErrors()...) {
                break
            }
        }
    }
    return errs
}

func (processor *processor) decode() (errs []error) {
    if processor.checkSkipLeft() || processor.MetaValues == nil {
        return
    }

    if destroy := processor.MetaValues.Get("_destroy"); destroy != nil {
        return
    }

    newRecord := true
    scope := &gorm.Scope{Value: processor.Result}
    if primaryField := scope.PrimaryField(); primaryField != nil {
        if !primaryField.IsBlank {
            newRecord = false
        } else {
            for _, metaValue := range processor.MetaValues.Values {
                if metaValue.Meta != nil && metaValue.Meta.GetFieldName() == primaryField.Name {
                    if v := utils.ToString(metaValue.Value); v != "" && v != "0" {
                        newRecord = false
                    }
                }
            }
        }
    }

    for _, metaValue := range processor.MetaValues.Values {
        meta := metaValue.Meta
        if meta == nil {
            continue
        }

        if newRecord && !meta.HasPermission(roles.Create, processor.Context) {
            continue
        } else if !newRecord && !meta.HasPermission(roles.Update, processor.Context) {
            continue
        }

        if setter := meta.GetSetter(); setter != nil {
            setter(processor.Result, metaValue, processor.Context)
        }

        if metaValue.MetaValues != nil && len(metaValue.MetaValues.Values) > 0 {
            if res := metaValue.Meta.GetResource(); res != nil && !reflect.ValueOf(res).IsNil() {
                field := reflect.Indirect(reflect.ValueOf(processor.Result)).FieldByName(meta.GetFieldName())
                // Only decode nested meta value into struct if no Setter defined
                if meta.GetSetter() == nil || reflect.Indirect(field).Type() == utils.ModelType(res.NewStruct()) {
                    if _, ok := field.Addr().Interface().(sql.Scanner); !ok {
                        decodeMetaValuesToField(res, field, metaValue, processor.Context)
                    }
                }
            }
        }
    }

    return
}

// Start start processor
func (processor *processor) Start() error {
    var errs qor.Errors
    processor.Initialize()
    if errs.AddError(processor.Validate()); !errs.HasError() {
        errs.AddError(processor.Commit())
    }
    if errs.HasError() {
        return errs
    }
    return nil
}

// Commit commit data into result
func (processor *processor) Commit() error {
    var errs qor.Errors
    errs.AddError(processor.decode()...)
    if processor.checkSkipLeft(errs.GetErrors()...) {
        return nil
    }

    for _, p := range processor.Resource.GetResource().Processors {
        if err := p.Handler(processor.Result, processor.MetaValues, processor.Context); err != nil {
            if processor.checkSkipLeft(err) {
                break
            }
            errs.AddError(err)
        }
    }
    return errs
}