protoreflect icon indicating copy to clipboard operation
protoreflect copied to clipboard

In deprecating old APIs, what would be a reasonable upgrade for "github.com/jhump/protoreflect/desc" and "github.com/jhump/protoreflect/dynamic"?

Open balena-zh opened this issue 11 months ago • 1 comments

I'm looking for ways to upgrade constructions such as those found in https://github.com/fullstorydev/grpcurl/blob/b9a11e9fea796d968e2dd83573312ead4b6fd831/desc_source.go.

In summary, I'm looking for ways to attend the interface grpcurl.DescriptorSource out of a protoregistry.GlobalFiles. Is there a simple way or would it involve deeper changes in grpcurl?

Example:

import (
	...
	"github.com/fullstorydev/grpcurl"
	"github.com/jhump/protoreflect/desc"
	"github.com/jhump/protoreflect/dynamic"

	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
	...
)

type fileSource struct {
	files  map[string]*desc.FileDescriptor
	er     *dynamic.ExtensionRegistry
	erInit sync.Once
}

func (fs *fileSource) ListServices() ([]string, error) {
	set := map[string]bool{}
	for _, fd := range fs.files {
		for _, svc := range fd.GetServices() {
			set[svc.GetFullyQualifiedName()] = true
		}
	}
	sl := make([]string, 0, len(set))
	for svc := range set {
		sl = append(sl, svc)
	}
	return sl, nil
}

func (fs *fileSource) GetAllFiles() ([]*desc.FileDescriptor, error) {
	files := make([]*desc.FileDescriptor, len(fs.files))
	i := 0
	for _, fd := range fs.files {
		files[i] = fd
		i++
	}
	return files, nil
}

func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
	for _, fd := range fs.files {
		if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil {
			return dsc, nil
		}
	}
	return nil, fmt.Errorf("symbol not found %v", fullyQualifiedName)
}

func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
	fs.erInit.Do(func() {
		fs.er = &dynamic.ExtensionRegistry{}
		for _, fd := range fs.files {
			fs.er.AddExtensionsFromFile(fd)
		}
	})
	return fs.er.AllExtensionsForType(typeName), nil
}

Which is consumed like

func DescriptorSourceFromGlobalRegistry() grpcurl.DescriptorSource {
	files := []*desc.FileDescriptor{}
	protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
		wrap, err := desc.WrapFile(fd)
		if err != nil {
			return false
		}
		name := wrap.GetName()
		if !strings.HasPrefix(name, "proto/") {
			return true
		}
		files = append(files, wrap)
		return true
	})

	fds := map[string]*desc.FileDescriptor{}
	for _, fd := range files {
		addFile(fd, fds)
	}
	return &fileSource{files: fds}
}

func addFile(fd *desc.FileDescriptor, fds map[string]*desc.FileDescriptor) {
	name := fd.GetName()
	if _, ok := fds[name]; ok {
		return
	}
	fds[name] = fd
	for _, dep := range fd.GetDependencies() {
		addFile(dep, fds)
	}
}

balena-zh avatar Jan 02 '25 14:01 balena-zh

In summary, I'm looking for ways to attend the interface grpcurl.DescriptorSource out of a protoregistry.GlobalFiles. Is there a simple way or would it involve deeper changes in grpcurl?

You would use the desc.WrapDescriptor or desc.WrapFile to adapt the protoreflect.Descriptor types, returned from a *protoregistry.Files, to the desc.Descriptor types.

Ideally, the grpcurl module would be updated to directly use the google.golang.org/protobuf module and its reflection types instead of the github.com/jhump/protoreflect/desc types. In general, APIs that directly refer to types in github.com/jhump/protoreflect likely need a v2 release that instead refers to the types in the protobuf runtime. The reason they do not is that they were written before the google.golang.org/protobuf was first released and before it had any reflection support.

jhump avatar Jan 06 '25 15:01 jhump