/* AfterTransaction will call f directly if self is not currently running a transaction, otherwise it will be appended to a slice of funcs run after every transaction is finished. */ func (self *DefaultContext) AfterTransaction(f interface{}) (err error) { // validate that f take one argument of whatever type and returns an error if err = utils.ValidateFuncInput(f, []reflect.Type{ reflect.TypeOf((*interface{})(nil)).Elem(), }); err != nil { return } if err = utils.ValidateFuncOutput(f, []reflect.Type{ reflect.TypeOf((*error)(nil)).Elem(), }); err != nil { return } // validate that whatever argument f took, a GAEContext would implement it if !reflect.TypeOf((*GAEContext)(nil)).Elem().AssignableTo(reflect.TypeOf(f).In(0)) { err = errors.Errorf("%v does not take an argument that is satisfied by %v", f, self) return } // create our after func afterFunc := func(c GAEContext) (err error) { if errVal := reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(c)})[0]; !errVal.IsNil() { err = errVal.Interface().(error) } return } if self.inTransaction { self.afterTransaction = append(self.afterTransaction, afterFunc) } else { if err = afterFunc(self); err != nil { return } } return }
/* CallTransactionFunction will validate that f takes something that implements a GAEContext, and that it returns an error. Then it will call f with c and return the error. */ func CallTransactionFunction(c GAEContext, f interface{}) (err error) { if err = utils.ValidateFuncInput(f, []reflect.Type{ reflect.TypeOf((*GAEContext)(nil)).Elem(), }); err != nil { err = errors.Wrap(err, 1) return } if err = utils.ValidateFuncOutput(f, []reflect.Type{ reflect.TypeOf((*error)(nil)).Elem(), }); err != nil { err = errors.Wrap(err, 1) return } if errVal := reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(c)})[0]; !errVal.IsNil() { return errVal.Interface().(error) } return nil }
/* MarshalJSON will recursively run any found `BeforeMarshal` functions on the content with arg and a stack of container instances, and then json marshal it. It will not recurse down further after a BeforeMarshal function has been found, but it will run all top level BeforeMarshal functions that it finds. */ func (self *DefaultJSONContext) MarshalJSON(c interface{}, body interface{}, arg interface{}) (result []byte, err error) { // declare a function that recursively will run itself var runRecursive func(reflect.Value, reflect.Value) error cVal := reflect.ValueOf(c) contextType := reflect.TypeOf((*JSONContextLogger)(nil)).Elem() // initialize an empty container stack stackType := reflect.TypeOf([]interface{}{}) // implement the function runRecursive = func(val reflect.Value, stack reflect.Value) error { // push this instance to the stack stack = reflect.Append(stack, val) // Try run BeforeMarshal fun := val.MethodByName("BeforeMarshal") if fun.IsValid() && !utils.IsNil(val.Interface()) { // make sure we don't run BeforeMarshal on any other things at the same time, at least in this context. return self.marshalSyncLock.Sync(val.Interface(), func() (err error) { // Validate BeforeMarshal takes something that implements JSONContextLogger if err = utils.ValidateFuncInput(fun.Interface(), []reflect.Type{contextType, stackType}); err != nil { if err = utils.ValidateFuncInput(fun.Interface(), []reflect.Type{contextType, stackType, reflect.TypeOf(arg)}); err != nil { return fmt.Errorf("BeforeMarshal needs to take an JSONContextLogger") } } // Validate BeforeMarshal returns an error if err = utils.ValidateFuncOutput(fun.Interface(), []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}); err != nil { return fmt.Errorf("BeforeMarshal needs to return an error") } args := []reflect.Value{cVal, stack} if fun.Type().NumIn() == 3 { args = append(args, reflect.ValueOf(arg)) } timer := time.Now() // run the actual BeforeMarshal res := fun.Call(args) if time.Now().Sub(timer) > (500 * time.Millisecond) { self.Infof("BeforeMarshal for %s is slow, took: %v", val.Type(), time.Now().Sub(timer)) } if !res[0].IsNil() { err = res[0].Interface().(error) } return }) } // Try do recursion on these types, if we didn't find a BeforeMarshal func on the val itself switch val.Kind() { case reflect.Ptr, reflect.Interface: if val.IsNil() { return nil } return runRecursive(val.Elem(), stack) case reflect.Slice: for i := 0; i < val.Len(); i++ { if err := runRecursive(val.Index(i).Addr(), stack); err != nil { return err } } break case reflect.Struct: for i := 0; i < val.NumField(); i++ { if val.Type().Field(i).PkgPath == "" { if err := runRecursive(val.Field(i), stack); err != nil { return err } } } break } return nil } // Run recursive reflection on self.Body that executes BeforeMarshal on every object possible. stack := []interface{}{} if err = runRecursive(reflect.ValueOf(body), reflect.ValueOf(stack)); err != nil { return } // This makes sure that replies that returns a slice that is empty returns a '[]' instad of 'null' bodyVal := reflect.ValueOf(body) if bodyVal.Kind() == reflect.Slice && bodyVal.IsNil() { reflect.ValueOf(&body).Elem().Set(reflect.MakeSlice(bodyVal.Type(), 0, 0)) } if result, err = json.MarshalIndent(body, "", " "); err != nil { return } return }