func ExampleGRPChw() { go servermain() time.Sleep(time.Second) l, nerr := glick.New(nil) if nerr != nil { log.Fatal(nerr) } var req pb.HelloRequest var err error if err = l.RegAPI("hw", &req, func() interface{} { var hr pb.HelloReply; return interface{}(&hr) }, 2*time.Second); err != nil { log.Fatal(err) } if err = ConfigGRPChw(l); err != nil { log.Fatal(err) } if err = l.Configure([]byte(`[ {"Plugin":"ExampleGRPChw","API":"hw","Actions":["hwAct"],"Type":"gRPChw","Path":"` + address + `"} ]`)); err != nil { log.Fatal(err) } req.Name = "gRPC" ctx, cancelCtx := context.WithTimeout(context.Background(), time.Second) defer cancelCtx() var repI interface{} repI, err = l.Run(ctx, "hw", "hwAct", &req) if err != nil { log.Fatal(err) } fmt.Println(repI.(*pb.HelloReply).Message) // output: Hello gRPC }
func TestOverloaderForever(t *testing.T) { l, nerr := glick.New(func(ctx context.Context, api, act string, handler glick.Plugin) (context.Context, glick.Plugin, error) { return ctx, nil, nil }) if nerr != nil { t.Error(nerr) } var prototype int if err := l.RegAPI("abc", prototype, func() interface{} { var b bool; return interface{}(&b) }, time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("abc", "forever", Forever, nil); err != nil { t.Error(err) return } ctx, can := context.WithTimeout(context.Background(), time.Millisecond) defer can() if _, err := l.Run(ctx, "abc", "forever", 1); err == nil { t.Error("overloader should have errored") return } }
func TestGetURL(t *testing.T) { outProtoString := func() interface{} { var s string; return interface{}(&s) } l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } proto := "" if err := l.RegAPI("string/*string", proto, outProtoString, 2*time.Second); err != nil { t.Error(err) } if err := l.RegPlugin("string/*string", "bad1", glick.PluginGetURL(true, "", &proto), nil); err == nil { t.Error("empty url not errored") } if err := l.RegPlugin("string/*string", "dynamic1", glick.PluginGetURL(false, "", &proto), nil); err != nil { t.Error(err) } if err := l.RegPlugin("string/*string", "documize", glick.PluginGetURL(true, "https://documize.com", &proto), nil); err != nil { t.Error(err) } if _, err := l.Run(nil, "string/*string", "documize", ""); err != nil { t.Error(err) } if _, err := l.Run(nil, "string/*string", "dynamic1", "http://golang.org"); err != nil { t.Error(err) } if _, err := l.Run(nil, "string/*string", "dynamic1", ""); err == nil { t.Error("empty url did not error") } if _, err := l.Run(nil, "string/*string", "dynamic1", "!@£$%^&*()"); err == nil { t.Error("bad url did not error") } }
func TestBadInterface(t *testing.T) { l, errN := glick.New(nil) if errN != nil { t.Error(errN) } var proto string var ip int ipProto := func() interface{} { var i int; return interface{}(&i) } if err := l.RegAPI("int/&int", ip, ipProto, 3*time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("int/&int", "pwd", glick.PluginCmd([]string{"pwd"}, &proto), nil); err != nil { t.Error(err) return } if _, err := l.Run(nil, "int/&int", "pwd", 11); err == nil { t.Error("does not error on non string input value") return } if err := l.RegAPI("string/&int", proto, ipProto, 3*time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("string/&int", "pwd", glick.PluginCmd([]string{"pwd"}, &proto), nil); err != nil { t.Error(err) return } if _, err := l.Run(nil, "string/&int", "pwd", "foo"); err == nil { t.Error("does not error on non *string ouput value") return } }
func pieSwitchTest(t *testing.T, useJSON bool) { l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) return } if err := glpie.ConfigPIE(l); err != nil { t.Error(err) return } var proto string protoOut := func() interface{} { s := "" return interface{}(&s) } if err := l.RegAPI("string/&string", proto, protoOut, 10*time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("string/&string", "cmdBad", glpie.PluginPie(useJSON, "dingbat", []string{"doodah"}, protoOut), nil); err == nil { t.Error("garbage pie plugin did not fail") return } if _, err := l.Run(nil, "string/&string", "cmdBad", proto); err == nil { t.Error("bad command did not fail") return } api := fmt.Sprintf("API%v", useJSON) act := fmt.Sprintf("ACT%v", useJSON) tisOut := func() interface{} { return interface{}(&test.IntStr{}) } if err := l.RegAPI(api, test.IntStr{}, tisOut, 2*time.Second); err != nil { t.Error(err) return } cmdPath := "./_test/gob/gob" if useJSON { cmdPath = "./_test/json/json" } if err := l.RegPlugin(api, act, glpie.PluginPie(useJSON, "CI.CopyIntX", []string{cmdPath}, tisOut), nil); err != nil { t.Error("unable to create " + err.Error()) return } parTest(t, l, api, act, cmdPath, useJSON, tisOut) }
func servermain() { lib, nerr := glick.New(nil) if nerr != nil { panic(nerr) } if err := lib.RegAPI("api", uppercaseRequest{}, func() interface{} { return uppercaseResponse{} }, time.Second); err != nil { panic(err) } if err := lib.RegPlugin("api", "lc", func(ctx context.Context, in interface{}) (interface{}, error) { return uppercaseResponse{ V: strings.ToLower(in.(uppercaseRequest).S), }, nil }, nil); err != nil { panic(err) } ctx := context.Background() svc := stringService{} lowercaseHandler := httptransport.NewServer( ctx, glkit.MakeEndpoint(lib, "api", "lc"), decodeUppercaseRequest, encodeResponse, ) uppercaseHandler := httptransport.NewServer( ctx, makeUppercaseEndpoint(svc), decodeUppercaseRequest, encodeResponse, ) countHandler := httptransport.NewServer( ctx, makeCountEndpoint(svc), decodeCountRequest, encodeResponse, ) http.Handle("/uppercase", uppercaseHandler) http.Handle("/lowercase", lowercaseHandler) http.Handle("/count", countHandler) log.Fatal(http.ListenAndServe("localhost:8080", nil)) }
func client(t *testing.T, useJSON bool, tisOut func() interface{}, endPt string) { l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } api := "ab" act := "cdef" if err := l.RegAPI(api, test.IntStr{}, tisOut, 2*time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin(api, act, glick.PluginRPC(useJSON, "CI.CopyIntX", endPt, tisOut), nil); err != nil { t.Error("unable to create JsonRPC " + err.Error()) return } par := test.IntStr{I: 42} if ret, err := l.Run(nil, api, act, par); err != nil { t.Error("unable to run plugin " + err.Error()) } else { if ret.(*test.IntStr).I != 42 { t.Error("RPC integer copy did not work") } } par.I = 4 if _, err := l.Run(nil, api, act, par); err == nil { t.Error("over-long plugin did not timeout") } if err := l.RegPlugin(api, "bep", glick.PluginRPC(useJSON, "", "localhost:8080", tisOut), nil); err == nil { t.Error("able to create empty end-point method") return } if err := l.RegPlugin(api, "bep", glick.PluginRPC(useJSON, "CI.CopyIntX", "", tisOut), nil); err == nil { t.Error("able to create empty endpoint") return } }
func TestGoKitStringsvc1(t *testing.T) { go servermain() <-time.After(2 * time.Second) l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } if err := glkit.ConfigKit(l); err != nil { t.Error(err) } if err := l.RegAPI("uppercase", uppercaseRequest{}, func() interface{} { return &uppercaseResponse{} }, time.Second); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"gk","API":"uppercase","Actions":["uc"],"Type":"KIT","Path":"http://localhost:8080/uppercase","JSON":true}, {"Plugin":"gk","API":"uppercase","Actions":["lc"],"Type":"KIT","Path":"http://localhost:8080/lowercase","JSON":true} ]`)); err != nil { t.Error(err) } if rep, err := l.Run(nil, "uppercase", "uc", uppercaseRequest{S: "abc"}); err == nil { if rep.(*uppercaseResponse).V != "ABC" { t.Error("uppercase did not work") } } else { t.Error(err) } if rep, err := l.Run(nil, "uppercase", "lc", uppercaseRequest{S: "XYZ"}); err == nil { if rep.(*uppercaseResponse).V != "xyz" { t.Error("lowercase did not work") } } else { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"gk","API":"uppercase","Actions":["uc"],"Type":"KIT","Path":"http://localhost:8080/uppercase","Gob":true} ]`)); err == nil { t.Error("did not spot non-JSON") } testCount(t) }
func TestTimeout(t *testing.T) { l, errN := glick.New(nil) if errN != nil { t.Error(errN) } var proto string outProto := func() interface{} { var s string; return interface{}(&s) } if err := l.RegAPI("alwaysTimeout", proto, outProto, 1*time.Second); err != nil { t.Error(err) } if err := l.RegPlugin("alwaysTimeout", "sleep", glick.PluginCmd([]string{"sleep", "10"}, &proto), nil); err != nil { t.Error(err) return } if _, err := l.Run(nil, "alwaysTimeout", "sleep", "foo"); err == nil { t.Error("does not timeout when it should") } }
func TestDup(t *testing.T) { l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } var d struct{} if er0 := l.RegAPI("A", d, func() interface{} { var s struct{}; return interface{}(&s) }, time.Second); er0 != nil { t.Error("register API gives error") } if er1 := l.RegPlugin("A", "B", Simp, nil); er1 != nil { t.Error("first entry gives error") } er2 := l.RegPlugin("A", "B", Simp, nil) if er2 != nil { t.Error("second entry should not give error") } }
func TestCmd(t *testing.T) { l, errN := glick.New(nil) if errN != nil { t.Error(errN) } var proto string outProto := func() interface{} { var s string; return interface{}(&s) } if err := l.RegAPI("string/&string", proto, outProto, 10*time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("string/&string", "pwdBad", glick.PluginCmd([]string{"pwdBad"}, &proto), nil); err == nil { t.Error("incorrect cmd plugin did not fail") return } if _, err := l.Run(nil, "string/&string", "pwdBad", proto); err == nil { t.Error("bad simple command did not fail") return } if err := l.RegPlugin("string/&string", "pwd", glick.PluginCmd([]string{"pwd"}, &proto), nil); err != nil { t.Error(err) return } if proto, err := l.Run(nil, "string/&string", "pwd", proto); err != nil { t.Error(err) } else { p := *proto.(*string) if !strings.HasSuffix(p, "/glick\n") { t.Error("wrong output from pwd: " + p) } } if err := l.RegPlugin("string/&string", "exit1", glick.PluginCmd([]string{"bash", "./_test/exit1.sh"}, &proto), nil); err != nil { t.Error(err) return } if _, err := l.Run(nil, "string/&string", "exit1", proto); err == nil { t.Error("exit1.sh does not give an error") } }
func clientBad(t *testing.T, useJSON bool, tisOut func() interface{}, endPt string) { l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } api := "ab" act := "cdef" if err := l.RegAPI(api, test.IntStr{}, tisOut, 2*time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin(api, act, glick.PluginRPC(useJSON, "CI.CopyIntX", endPt, tisOut), nil); err != nil { t.Error("unable to create JsonRPC " + err.Error()) return } par := test.IntStr{I: 42} if err := l.RegPlugin(api, "errEP", glick.PluginRPC(useJSON, "CI.CopyIntX", "localhost:9999", tisOut), nil); err != nil { t.Error("error on valid (if unused) endpoint") return } if _, err := l.Run(nil, api, "errEP", par); err == nil { t.Error("did not error on unpopulated end-point") } noPoint := func() interface{} { return interface{}(42) } if err := l.RegAPI("noPoint", 42, noPoint, 0); err != nil { t.Error(err) } if err := l.RegPlugin("noPoint", "errEP", glick.PluginRPC(useJSON, "CI.CopyIntX", "localhost:9999", noPoint), nil); err == nil { t.Error("a non-pointer return should error") } }
func TestConfig2(t *testing.T) { l, ne := glick.New(nil) if ne != nil { t.Error(ne) } protoString := "" outProtoString := func() interface{} { var s string; return interface{}(&s) } outProtoInt := func() interface{} { var i int; return interface{}(&i) } if err := l.RegAPI("string/*string", protoString, outProtoString, 0); err != nil { t.Error(err) } var is test.IntStr if err := l.RegAPI("test", is, outProtoInt, 0); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"p66","API":"test","Actions":["intStr3"],"Type":"RPC","Path":"localhost:4242","Method":"foo.bar","Token":"ABC"} ]`)); err != nil { t.Error(err) } if l.Token("test", "intStr3") != "ABC" { t.Error("Token value not set and retrieved") } if err := l.Configure([]byte(`[ {"Plugin":"p74","API":"test","Actions":["intStr4"],"Type":"RPC","Path":"foo;;:4242"} ]`)); err == nil { t.Error("unsuited endPoint not spotted") } if err := l.Configure([]byte(`[ {"Plugin":"p79","API":"string/*string","Actions":["goodURL"],"Type":"URL","Path":"http://golang.org","Static":true} ]`)); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"p84","API":"string/*string","Actions":["badURL"],"Type":"URL","Path":"","Static":true} ]`)); err == nil { t.Error("unsuited URL not spotted") } }
func TestConfig(t *testing.T) { l, ne := glick.New(nil) if ne != nil { t.Error(ne) } protoString := "" outProtoString := func() interface{} { var s string; return interface{}(&s) } if err := l.RegAPI("string/*string", protoString, outProtoString, 0); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"p34","API":"string/*string","Actions":["doIt"]} ]`)); err == nil { t.Error("missing Type not an error") } if err := l.Configure([]byte(`[ {"Plugin":"p39","API":"string/*string","Actions":["pwd"],"Type":"CMD","Cmd":["pwd"]} ]`)); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"p44","API":"string/*string","Actions":["garbage"],"Type":"CMD","Cmd":["garbage"]} ]`)); err == nil { t.Error("garbage cmd path did not error") } if err := l.RegAPI("int/*string", 42, outProtoString, 0); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"p52","API":"int/*string","Actions":["badAPI"],"Type":"CMD","Cmd":["pwd"]} ]`)); err == nil { t.Error("unsuited API for cmd did not error") } if err := l.Configure([]byte(`[ {"Plugin":"p57","API":"int/*string","Actions":["badAPI"],"Type":"URL","Path":["pwd"]} ]`)); err == nil { t.Error("unsuited API for URL did not error") } }
func TestOverloaderBad(t *testing.T) { l, nerr := glick.New(func(ctx context.Context, api, act string, handler glick.Plugin) (context.Context, glick.Plugin, error) { if api == "abc" && act == "bad" { return ctx, nil, errors.New("you done a bad... bad... thing") } return ctx, nil, nil }) if nerr != nil { t.Error(nerr) } var prototype int if err := l.RegAPI("abc", prototype, func() interface{} { var b bool; return interface{}(&b) }, time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("abc", "bad", Def, nil); err != nil { t.Error(err) return } if _, err := l.Run(nil, "abc", "bad", 1); err == nil { t.Error("overloader should have errored") return } ctx, can := context.WithTimeout(context.Background(), time.Millisecond) defer can() if err := l.RegPlugin("abc", "justBad", JustBad, nil); err != nil { t.Error(err) return } ctx, can = context.WithTimeout(context.Background(), time.Millisecond) defer can() if _, err := l.Run(ctx, "abc", "justBad", 1); err == nil { t.Error("overloader should have errored") return } }
func TestBadConfig(t *testing.T) { l, ne := glick.New(nil) if ne != nil { t.Error(ne) } if err := l.Configure([]byte("±§~`-=_+")); err == nil { t.Error("did not error on rubbish") } if err := l.Configure([]byte("[]")); err != nil { t.Error(err) } if err := l.Configure([]byte(`[ {"Plugin":"p22",API":"missing"} ]`)); err == nil { t.Error("missing API not an error") } if err := l.AddConfigurator("zombie", nil); err == nil { t.Error("nil configurator not spotted") } if err := glick.ConfigGetURL(l); err == nil { t.Error("duplicate configurator not spotted") } }
func TestAPI(t *testing.T) { l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } if err := l.RegAPI("z", nil, nil, time.Second); err != glick.ErrNilAPI { t.Error("does not return nil api error") } var dummy int outGood := func() interface{} { var d int; return interface{}(&d) } if err := l.RegAPI("z", dummy, outGood, time.Second); err != nil { t.Error("1st reg API returns error") } if err := l.RegAPI("z", dummy, outGood, time.Second); err == nil { t.Error("does not return duplicate api error") } if _, err := l.Run(nil, "z", "unknown", dummy); err == nil { t.Error("does not return no plugin") } if _, err := l.Run(nil, "unknown", "unknown", dummy); err == nil { t.Error("does not return unknown api error") } }
func TestGetURLint(t *testing.T) { outProtoString := func() interface{} { var s string; return interface{}(&s) } outProtoInt := func() interface{} { var i int; return interface{}(&i) } l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } proto := "" ip := 0 if err := l.RegAPI("int/*string", ip, outProtoString, 2*time.Second); err != nil { t.Error(err) } if err := l.RegPlugin("int/*string", "documize", glick.PluginGetURL(true, "https://documize.com", &proto), nil); err != nil { t.Error(err) } if _, err := l.Run(nil, "int/*string", "documize", 42); err == nil { t.Error("bad api in did not error") } if err := l.RegAPI("string/*int", proto, outProtoInt, 2*time.Second); err != nil { t.Error(err) } if err := l.RegPlugin("string/*int", "documize", glick.PluginGetURL(true, "https://documize.com", &proto), nil); err != nil { t.Error(err) } if _, err := l.Run(nil, "string/*int", "documize", ""); err == nil { t.Error("bad api out did not error") } if _, err := l.Run(nil, "string/*string", "documize", nil); err == nil { t.Error("nil input value did not error") } if _, err := l.Run(nil, "string/*string", "documize", true); err == nil { t.Error("incorrect input value did not error") } }
func TestOverloaderMOL(t *testing.T) { hadOvStub := Tov l, nerr := glick.New(func(ctx context.Context, api, act string, handler glick.Plugin) (context.Context, glick.Plugin, error) { if api == "abc" && act == "meaning-of-life" { return ctx, hadOvStub, nil } return ctx, nil, nil }) if nerr != nil { t.Error(nerr) } var prototype int if err := l.RegAPI("abc", prototype, func() interface{} { var b bool; return interface{}(&b) }, time.Second); err != nil { t.Error(err) return } if err := l.RegPlugin("abc", "default", Def, nil); err != nil { t.Error(err) return } if ret, err := l.Run(nil, "abc", "default", 1); err != nil { t.Error(err) } else { if *ret.(*bool) { t.Error("Overloaded function called in error") } } if ret, err := l.Run(nil, "abc", "meaning-of-life", 1); err != nil { t.Error(err) } else { if !*ret.(*bool) { t.Error("Overloaded function not called") } } }
func TestSimple(t *testing.T) { l, nerr := glick.New(nil) if nerr != nil { t.Error(nerr) } api := "S" var i int if err := l.RegPlugin("unknown", "Test", Simp, nil); err == nil { t.Error("register plugin does not give unknown API error") } if err := l.RegAPI(api, i, outSimp, time.Second); err != nil { t.Error(err) return } if er1 := l.RegPlugin(api, "Test", Simp, nil); er1 != nil { t.Error("register gives error", er1) } if ret, err := l.Run(nil, api, "Test", 42); err != nil { t.Error(err) } else { if *ret.(*int) != 42 { t.Error("called plugin did not work") } } if ppo, err := l.ProtoPlugOut(api); err == nil { if reflect.TypeOf(ppo()) != reflect.TypeOf(outSimp()) { t.Error("wrong proto type") } } else { t.Error(err) } if _, err := l.ProtoPlugOut("Sinbad"); err == nil { t.Error("no error for non-existant api") } }
func Example() { goDatePlugin := func(ctx context.Context, in interface{}) (interface{}, error) { return time.Now().String(), nil } runtimeRerouter := func(ctx context.Context, api, action string, handler glick.Plugin) (context.Context, glick.Plugin, error) { // if we hit a particular set of circumstances return the go version if ctx.Value("bingo") != nil && api == "timeNow" && action == "lookup" { return ctx, goDatePlugin, nil } // otherwise return what we we were planning to do anyway return ctx, handler, nil } lib, nerr := glick.New(runtimeRerouter) if nerr != nil { fmt.Println(nerr) return } timeNowAPIproto := "" if err := lib.RegAPI("timeNow", timeNowAPIproto, func() interface{} { return timeNowAPIproto }, time.Second); err != nil { fmt.Println(err) return } // the set-up version of the plugin, in Go if err := lib.RegPlugin("timeNow", "lookup", goDatePlugin, nil); err != nil { fmt.Println(err) return } ctx := context.Background() lookup := func() { if S, err := lib.Run(ctx, "timeNow", "lookup", ""); err != nil { fmt.Println(err) } else { fmt.Println(S) } } lookup() // should run the go version // now overload an os version of timeNow/lookup via a JSON config if err := lib.Configure([]byte(`[ {"Plugin":"OS-style-date","API":"timeNow","Actions":["lookup"],"Type":"CMD","Cmd":["date"]} ]`)); err != nil { fmt.Println(err) } lookup() // should run the os command 'date' and print the output // now set a specific context to be picked-up in runtimeRerouter ctx = context.WithValue(ctx, "bingo", "house") lookup() // should run the go version again after being re-routed }