func convertValToMap(v *otto.Value) map[string]interface{} { if v.IsDefined() && v.IsObject() { options, _ := v.Export() return options.(map[string]interface{}) } return make(map[string]interface{}) }
// find the node from the based ont the hash passed in // the hash needs to at least have a {name: }property func (js *JavascriptBuilder) findNode(token string, in otto.Value) (n Node, err error) { var ( givenOptions map[string]interface{} ok bool name string ) e, err := in.Export() if err != nil { return n, err } // accept both a json hash and a string as an argument. // if the arg is a hash, then we should extract the name, // and pull the node from the yaml, and then merge the given options // over top of the options presented in the config node. // // if the arg is a string, then use that string as the name // and pull the config node switch arg := e.(type) { case map[string]interface{}: givenOptions = arg if name, ok = givenOptions["name"].(string); ok { // merge the two maps tmpMap := make(map[string]interface{}) for k, v := range js.config.Nodes[name] { tmpMap[k] = v } for k, v := range givenOptions { tmpMap[k] = v } givenOptions = tmpMap } else { // we don't have a name, so lets generate one. u, err := uuid.NewV4() if err != nil { return n, fmt.Errorf("%s error. unable to create uuid (%s)", token, err.Error()) } name = u.String() givenOptions["name"] = name } case string: name = arg givenOptions, ok = js.config.Nodes[name] if !ok { return n, fmt.Errorf("%s error. unable to find node '%s'", token, name) } } if token == "transform" { // this is a little bit of magic so that transformers (which are added by the transform fn get the right kind) givenOptions["type"] = "transformer" } kind, ok := givenOptions["type"].(string) if !ok { return n, fmt.Errorf("%s: hash requires a type field, but no type given", token) } return NewNode(name, kind, givenOptions) }
//GetBool gets bool from otto value func GetBool(value otto.Value) (bool, error) { rawBool, _ := value.Export() result, ok := rawBool.(bool) if !ok { return false, fmt.Errorf(wrongArgumentType, rawBool, "bool") } return result, nil }
//GetTransaction gets Transaction from otto value func GetTransaction(value otto.Value) (transaction.Transaction, error) { rawTransaction, _ := value.Export() result, ok := rawTransaction.(transaction.Transaction) if !ok { return nil, fmt.Errorf(wrongArgumentType, rawTransaction, "Transaction") } return result, nil }
//GetAuthorization gets Transaction from otto value func GetAuthorization(value otto.Value) (schema.Authorization, error) { rawAuthorization, _ := value.Export() result, ok := rawAuthorization.(schema.Authorization) if !ok { return nil, fmt.Errorf(wrongArgumentType, rawAuthorization, "Authorization") } return result, nil }
//GetString gets string from otto value func GetString(value otto.Value) (string, error) { rawString, _ := value.Export() result, ok := rawString.(string) if !ok { return "", fmt.Errorf(wrongArgumentType, rawString, "string") } return result, nil }
//GetStringList gets []string from otto value func GetStringList(value otto.Value) ([]string, error) { rawSlice, _ := value.Export() rawResult, ok := rawSlice.([]string) if !ok { return make([]string, 0), fmt.Errorf(wrongArgumentType, rawSlice, "array of strings") } return rawResult, nil }
func (f *JSFilter) Filter(ev *message.Event) (*message.Event, error) { var dropped bool f.vm.Set("$", map[string]interface{}{ "env": f.conf.Env, "event": map[string]interface{}{ "tag": ev.Tag, "time": ev.Time, "record": ev.Record, }, "drop": func(call otto.FunctionCall) otto.Value { dropped = true return otto.Value{} }, "emit": func(call otto.FunctionCall) (ret otto.Value) { var record otto.Value var t time.Time tag := call.Argument(0).String() if !call.Argument(2).IsDefined() { record = call.Argument(1) t = time.Now() } else { record = call.Argument(2) v, err := call.Argument(1).Export() if err != nil { f.env.Log.Warningf("Failed to get time: %v", err) return } var ok bool t, ok = v.(time.Time) if !ok { f.env.Log.Warningf("Failed to get time: unsupported type %T", v) return } } rec, err := record.Export() if err != nil { f.env.Log.Warningf("Failed to get record: %v", err) return } typedRec, ok := rec.(map[string]interface{}) if !ok { f.env.Log.Warningf("Failed to get record: Unsupported type %T", rec) return } f.env.Emit(message.NewEventWithTime(tag, t, typedRec)) return }, }) _, err := f.vm.Run(f.script) if err != nil { return nil, err } else if dropped { return nil, nil } return ev, nil }
//GetMap gets map[string]interface{} from otto value func GetMap(value otto.Value) (map[string]interface{}, error) { rawMap, _ := value.Export() result, ok := rawMap.(map[string]interface{}) if !ok { return map[string]interface{}{}, fmt.Errorf(wrongArgumentType, rawMap, "Object") } for key, value := range result { result[key] = ConvertOttoToGo(value) } return result, nil }
// Converts a JS string or array into a Go string array. func ottoValueToStringArray(value otto.Value) []string { nativeValue, _ := value.Export() result := base.ValueToStringArray(nativeValue) if result == nil && !value.IsNull() && !value.IsUndefined() { base.Warn("SyncRunner: Non-string, non-array passed to JS callback: %s", value) } return result }
//GetList gets []interface{} from otto value func GetList(value otto.Value) ([]interface{}, error) { rawSlice, err := value.Export() result := make([]interface{}, 0) if rawSlice == nil || err != nil { return result, err } typeOfSlice := reflect.TypeOf(rawSlice) if typeOfSlice.Kind() != reflect.Array && typeOfSlice.Kind() != reflect.Slice { return result, fmt.Errorf(wrongArgumentType, value, "array") } list := reflect.ValueOf(rawSlice) for i := 0; i < list.Len(); i++ { result = append(result, ConvertOttoToGo(list.Index(i).Interface())) } return result, err }
func ottoValueToStringArray(value otto.Value) []string { { value, _ := value.Export() switch value := value.(type) { case string: return []string{value} case []interface{}: result := make([]string, 0, len(value)) for _, item := range value { if value, ok := item.(string); ok { result = append(result, value) } } return result } } return nil }
func objToHeader(data *otto.Value, header http.Header) { dataMap, _ := data.Export() for key, val := range dataMap.(map[string]interface{}) { switch val.(type) { case string: { header.Add(key, val.(string)) break } case []interface{}: { for _, headerVal := range val.([]interface{}) { header.Add(key, headerVal.(string)) } break } } } }
func buildIteratorFromValue(val otto.Value, ts graph.TripleStore) graph.Iterator { if val.IsNull() || val.IsUndefined() { return ts.GetNodesAllIterator() } if val.IsPrimitive() { thing, _ := val.Export() switch v := thing.(type) { case string: it := ts.MakeFixed() it.AddValue(ts.GetIdFor(v)) return it default: glog.Errorln("Trying to build unknown primitive value.") } } switch val.Class() { case "Object": return buildIteratorTree(val.Object(), ts) case "Array": // Had better be an array of strings strings := makeListOfStringsFromArrayValue(val.Object()) it := ts.MakeFixed() for _, x := range strings { it.AddValue(ts.GetIdFor(x)) } return it case "Number": fallthrough case "Boolean": fallthrough case "Date": fallthrough case "String": it := ts.MakeFixed() str, _ := val.ToString() it.AddValue(ts.GetIdFor(str)) return it default: glog.Errorln("Trying to handle unsupported Javascript value.") return graph.NewNullIterator() } }
//GetStringList gets []string from otto value func GetStringList(value otto.Value) ([]string, error) { var ok bool var rawSlice []interface{} var stringSlice []string rawData, _ := value.Export() rawSlice, ok = rawData.([]interface{}) if ok && len(rawSlice) == 0 { return []string{}, nil } stringSlice, ok = rawData.([]string) if !ok { return make([]string, 0), fmt.Errorf(wrongArgumentType, rawData, "array of strings") } return stringSlice, nil }
// Converts a JS array into a Go string array. func ottoArrayToStrings(array otto.Value) []string { goValue, err := array.Export() if err != nil { return nil } if result, ok := goValue.([]string); ok { return result } goArray, ok := goValue.([]interface{}) if !ok || len(goArray) == 0 { return nil } result := make([]string, 0, len(goArray)) for _, item := range goArray { if str, ok := item.(string); ok { result = append(result, str) } } return result }
func buildIteratorFromValue(val otto.Value, qs graph.QuadStore) graph.Iterator { if val.IsNull() || val.IsUndefined() { return qs.NodesAllIterator() } if val.IsPrimitive() { thing, _ := val.Export() switch v := thing.(type) { case string: it := qs.FixedIterator() it.Add(qs.ValueOf(v)) return it default: glog.Errorln("Trying to build unknown primitive value.") } } switch val.Class() { case "Object": return buildIteratorTree(val.Object(), qs) case "Array": // Had better be an array of strings strings := stringsFrom(val.Object()) it := qs.FixedIterator() for _, x := range strings { it.Add(qs.ValueOf(x)) } return it case "Number": fallthrough case "Boolean": fallthrough case "Date": fallthrough case "String": it := qs.FixedIterator() it.Add(qs.ValueOf(val.String())) return it default: glog.Errorln("Trying to handle unsupported Javascript value.") return iterator.NewNull() } }
// Converts a JS string or array into a Go string array. func ottoValueToStringArray(value otto.Value) []string { nativeValue, _ := value.Export() switch nativeValue := nativeValue.(type) { case string: return []string{nativeValue} case []string: return nativeValue case []interface{}: result := make([]string, 0, len(nativeValue)) for _, item := range nativeValue { if str, ok := item.(string); ok { result = append(result, str) } } return result default: if !value.IsNull() && !value.IsUndefined() { base.Warn("SyncRunner: Non-string, non-array passed to JS callback: %s", value) } return nil } }
func buildPathFromValue(val otto.Value) (out []interface{}) { if val.IsNull() || val.IsUndefined() { return nil } if val.IsPrimitive() { thing, _ := val.Export() switch v := thing.(type) { case string: out = append(out, v) return default: clog.Errorf("Trying to build unknown primitive value.") } } switch val.Class() { case "Object": out = append(out, buildPathFromObject(val.Object())) return case "Array": // Had better be an array of strings for _, x := range stringsFrom(val.Object()) { out = append(out, x) } return case "Number": fallthrough case "Boolean": fallthrough case "Date": fallthrough case "String": out = append(out, val.String()) return default: clog.Errorf("Trying to handle unsupported Javascript value.") return nil } }
func (t *Transformer) transformOne(msg *message.Msg) (*message.Msg, error) { var ( doc interface{} value otto.Value outDoc otto.Value result interface{} err error ) // short circuit for deletes and commands if msg.Op == message.Command { return msg, nil } now := time.Now().Nanosecond() fullDoc := map[string]interface{}{ "data": msg.Data, "ts": msg.Timestamp, "op": msg.Op.String(), } if msg.IsMap() { if doc, err = mejson.Marshal(msg.Data); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } fullDoc["data"] = doc } if value, err = t.vm.ToValue(fullDoc); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } // now that we have finished casting our map to a bunch of different types, // lets run our transformer on the document beforeVM := time.Now().Nanosecond() if outDoc, err = t.vm.Call(`module.exports`, nil, value); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } if result, err = outDoc.Export(); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } afterVM := time.Now().Nanosecond() fullDoc, ok := result.(map[string]interface{}) if !ok { t.pipe.Err <- t.transformerError(ERROR, fmt.Errorf("returned doc was not a map[string]interface{}"), msg) return msg, fmt.Errorf("returned doc was not a map[string]interface{}") } msg.Op = message.OpTypeFromString(fullDoc["op"].(string)) msg.Timestamp = fullDoc["ts"].(int64) switch data := fullDoc["data"].(type) { case otto.Value: exported, err := data.Export() if err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } d, err := mejson.Unmarshal(exported.(map[string]interface{})) if err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } msg.Data = map[string]interface{}(d) case map[string]interface{}: d, err := mejson.Unmarshal(data) if err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } msg.Data = map[string]interface{}(d) default: msg.Data = data } if t.debug { then := time.Now().Nanosecond() fmt.Printf("document transformed in %dus. %d to marshal, %d in the vm, %d to unmarshal\n", (then-now)/1000, (beforeVM-now)/1000, (afterVM-beforeVM)/1000, (then-afterVM)/1000) } return msg, nil }
func (R *RuleEngine) Execute(fact *Fact, extf func(*Fact)) { R.Otto.Set("LOG", func(call otto.FunctionCall) otto.Value { log.Println(call.Argument(0).ToString()) return otto.UndefinedValue() }) complete := false (*fact)["result"] = true session := copyFact(*fact) lastSession := copyFact(*fact) index := 0 RESTART := func() { index = 0 } NEXT := func() { if R.IgnoreFactChanges || lastSession.equals(session) { index += 1 } else { lastSession = copyFact(*session) RESTART() } } STOP := func() { complete = true RESTART() } WHEN := func(ruleIndex int, outcome bool) { if outcome { if val, ok := R.ActiveRules[ruleIndex]["consequence"]; ok { if _consequence, ok := val.(string); ok { var err error if err = R.Otto.Set("RESTART", func(call otto.FunctionCall) otto.Value { RESTART() return otto.UndefinedValue() }); err != nil { log.Fatal(err.Error()) } if err = R.Otto.Set("STOP", func(call otto.FunctionCall) otto.Value { STOP() return otto.UndefinedValue() }); err != nil { log.Fatal(err.Error()) } if err = R.Otto.Set("NEXT", func(call otto.FunctionCall) otto.Value { NEXT() return otto.UndefinedValue() }); err != nil { log.Fatal(err.Error()) } if _, err := R.Otto.Run(fmt.Sprintf("var F = %v;", toJSON(session))); err != nil { log.Fatal(err.Error()) } if _, err = R.Otto.Run(_consequence); err != nil { log.Fatal(err.Error()) } var ( oval otto.Value ival interface{} fval map[string]interface{} f Fact ) if oval, err = R.Otto.Get("F"); err != nil { log.Fatal(err.Error()) } if ival, err = oval.Export(); err != nil { log.Fatal(err.Error()) } if fval, ok = ival.(map[string]interface{}); !ok { log.Fatal("WHEN: F is not a Fact") } f = Fact(fval) session = &f } else { log.Fatal("WHEN: consequence not a string") } } else { log.Fatal("WHEN: rule has no field 'consequence'") } } else { NEXT() } } for index < len(R.ActiveRules) { if complete { break } if value, ok := R.ActiveRules[index]["condition"]; ok { if _condition, ok := value.(string); ok { var err error when := func(call otto.FunctionCall) otto.Value { if len(call.ArgumentList) == 1 && call.Argument(0).IsBoolean() { var b bool if b, err = call.Argument(0).ToBoolean(); err != nil { log.Fatal(err.Error()) } else { WHEN(index, b) } } else { log.Fatal("Execute: WHEN called with wrong argument(s)") } return otto.UndefinedValue() } if err := R.Otto.Set("WHEN", when); err != nil { log.Fatal(err.Error()) } if _, err = R.Otto.Run(fmt.Sprintf("var F = %v;", toJSON(session))); err != nil { log.Fatal(err.Error()) } if _, err = R.Otto.Run(_condition); err != nil { log.Fatal(err.Error()) } var ( oval otto.Value ival interface{} fval map[string]interface{} f Fact ) if oval, err = R.Otto.Get("F"); err != nil { log.Fatal(err.Error()) } if ival, err = oval.Export(); err != nil { log.Fatal(err.Error()) } if fval, ok = ival.(map[string]interface{}); !ok { log.Fatal("Execute: F is not a Fact") } f = Fact(fval) session = &f } else { log.Fatal("Execute: condition not a string") } } else { log.Fatal("Execute: rule has no field 'condition'") } } extf(session) }
func (t *Transformer) transformOne(msg *message.Msg) (*message.Msg, error) { var ( doc interface{} value otto.Value outDoc otto.Value result interface{} err error ) // short circuit for deletes and commands if msg.Op == message.Command { return msg, nil } now := time.Now().Nanosecond() currMsg := map[string]interface{}{ "data": msg.Data, "ts": msg.Timestamp, "op": msg.Op.String(), "ns": msg.Namespace, } if msg.IsMap() { if doc, err = mejson.Marshal(msg.Data); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } currMsg["data"] = doc } if value, err = t.vm.ToValue(currMsg); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } // now that we have finished casting our map to a bunch of different types, // lets run our transformer on the document beforeVM := time.Now().Nanosecond() if outDoc, err = t.vm.Call(`module.exports`, nil, value); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } if result, err = outDoc.Export(); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } afterVM := time.Now().Nanosecond() if err = t.toMsg(result, msg); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, err } if t.debug { then := time.Now().Nanosecond() fmt.Printf("document transformed in %dus. %d to marshal, %d in the vm, %d to unmarshal\n", (then-now)/1000, (beforeVM-now)/1000, (afterVM-beforeVM)/1000, (then-afterVM)/1000) } return msg, nil }
func (t *Transformer) transformOne(msg *message.Msg) (*message.Msg, error) { var ( doc interface{} value otto.Value outDoc otto.Value result interface{} err error ) // short circuit for deletes and commands if msg.Op == message.Delete || msg.Op == message.Command { return msg, nil } now := time.Now().Nanosecond() if msg.IsMap() { if doc, err = mejson.Marshal(msg.Data); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } } else { doc = msg.Data } if value, err = t.vm.ToValue(doc); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } // now that we have finished casting our map to a bunch of different types, // lets run our transformer on the document beforeVM := time.Now().Nanosecond() if outDoc, err = t.vm.Call(`module.exports`, nil, value); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } if result, err = outDoc.Export(); err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } afterVM := time.Now().Nanosecond() switch r := result.(type) { case map[string]interface{}: doc, err := mejson.Unmarshal(r) if err != nil { t.pipe.Err <- t.transformerError(ERROR, err, msg) return msg, nil } msg.Data = map[string]interface{}(doc) default: msg.Data = r } if t.debug { then := time.Now().Nanosecond() fmt.Printf("document transformed in %dus. %d to marshal, %d in the vm, %d to unmarshal\n", (then-now)/1000, (beforeVM-now)/1000, (afterVM-beforeVM)/1000, (then-afterVM)/1000) } return msg, nil }