diff --git a/contrib/opensergo/README.md b/contrib/opensergo/README.md new file mode 100644 index 000000000..82cfeccee --- /dev/null +++ b/contrib/opensergo/README.md @@ -0,0 +1,32 @@ +# OpenSergo + +## Usage + +```go + osServer, err := opensergo.New(opensergo.WithEndpoint("localhost:9090")) + if err != nil { + panic("init opensergo error") + } + + s := &server{} + grpcSrv := grpc.NewServer( + grpc.Address(":9000"), + grpc.Middleware( + recovery.Recovery(), + ), + ) + helloworld.RegisterGreeterServer(grpcSrv, s) + + app := kratos.New( + kratos.Name(Name), + kratos.Server( + grpcSrv, + ), + ) + + osServer.ReportMetadata(context.Background(), app) + + if err := app.Run(); err != nil { + log.Fatal(err) + } +``` \ No newline at end of file diff --git a/contrib/opensergo/go.mod b/contrib/opensergo/go.mod index e1ebf519e..635de717e 100644 --- a/contrib/opensergo/go.mod +++ b/contrib/opensergo/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-kratos/kratos/v2 v2.2.2 github.com/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 + google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd google.golang.org/grpc v1.46.2 google.golang.org/protobuf v1.28.0 ) @@ -17,6 +18,5 @@ require ( golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/text v0.3.7 // indirect - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect gopkg.in/yaml.v3 v3.0.0 // indirect ) diff --git a/contrib/opensergo/opensergo.go b/contrib/opensergo/opensergo.go index 536a8d940..f22e16e0a 100644 --- a/contrib/opensergo/opensergo.go +++ b/contrib/opensergo/opensergo.go @@ -12,8 +12,10 @@ import ( "github.com/go-kratos/kratos/v2" v1 "github.com/opensergo/opensergo-go/proto/service_contract/v1" "golang.org/x/net/context" + "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" ) @@ -73,15 +75,18 @@ func New(opts ...Option) (*OpenSergo, error) { } func (s *OpenSergo) ReportMetadata(ctx context.Context, app kratos.AppInfo) error { - services, err := s.listServiceDescriptors() + services, types, err := listDescriptors() if err != nil { return err } + serviceMetadata := &v1.ServiceMetadata{ ServiceContract: &v1.ServiceContract{ Services: services, + Types: types, }, } + for _, endpoint := range app.Endpoint() { u, err := url.Parse(endpoint) //nolint if err != nil { @@ -104,11 +109,12 @@ func (s *OpenSergo) ReportMetadata(ctx context.Context, app kratos.AppInfo) erro _, err = s.mdClient.ReportMetadata(ctx, &v1.ReportMetadataRequest{ AppName: app.Name(), ServiceMetadata: []*v1.ServiceMetadata{serviceMetadata}, + // TODO: Node: *v1.Node, }) return err } -func (s *OpenSergo) listServiceDescriptors() (services []*v1.ServiceDescriptor, err error) { +func listDescriptors() (services []*v1.ServiceDescriptor, types []*v1.TypeDescriptor, err error) { protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool { for i := 0; i < fd.Services().Len(); i++ { var ( @@ -120,19 +126,78 @@ func (s *OpenSergo) listServiceDescriptors() (services []*v1.ServiceDescriptor, mName := string(md.Name()) inputType := string(md.Input().FullName()) outputType := string(md.Output().FullName()) + isClientStreaming := md.IsStreamingClient() + isServerStreaming := md.IsStreamingServer() + pattern := proto.GetExtension(md.Options(), annotations.E_Http).(*annotations.HttpRule).GetPattern() + var httpPath, httpMethod string + if pattern != nil { + httpPath, httpMethod = HTTPPatternInfo(pattern) + } methodDesc := v1.MethodDescriptor{ - Name: mName, - InputTypes: []string{inputType}, - OutputTypes: []string{outputType}, + Name: mName, + InputTypes: []string{inputType}, + OutputTypes: []string{outputType}, + ClientStreaming: &isClientStreaming, + ServerStreaming: &isServerStreaming, + HttpPaths: []string{httpPath}, + HttpMethods: []string{httpMethod}, + // TODO: Description: *string, } methods = append(methods, &methodDesc) } services = append(services, &v1.ServiceDescriptor{ Name: string(sd.Name()), Methods: methods, + // TODO: Description: *string, + }) + } + + for i := 0; i < fd.Messages().Len(); i++ { + var ( + fields []*v1.FieldDescriptor + md = fd.Messages().Get(i) + ) + + for j := 0; j < md.Fields().Len(); j++ { + fd := md.Fields().Get(j) + kind := fd.Kind() + typeName := kind.String() + + fields = append(fields, &v1.FieldDescriptor{ + Name: string(fd.Name()), + Number: int32(fd.Number()), + Type: v1.FieldDescriptor_Type(kind), + TypeName: &typeName, + // TODO: Description: *string, + }) + } + + types = append(types, &v1.TypeDescriptor{ + Name: string(md.Name()), + Fields: fields, }) } + return true }) return } + +func HTTPPatternInfo(pattern interface{}) (method string, path string) { + switch p := pattern.(type) { + case *annotations.HttpRule_Get: + return "GET", p.Get + case *annotations.HttpRule_Post: + return "POST", p.Post + case *annotations.HttpRule_Delete: + return "DELETE", p.Delete + case *annotations.HttpRule_Patch: + return "PATCH", p.Patch + case *annotations.HttpRule_Put: + return "PUT", p.Put + case *annotations.HttpRule_Custom: + return p.Custom.Kind, p.Custom.Path + default: + return "", "" + } +}