This commit implements suppot for looking up Service descriptors in the global descriptor pool, and from there looking up the associated methods. This is implemented in each of the C extension, JRuby extension, and the FFI implementation. The descriptors can be used to look up the options on the service/methods, and also the request/response types and streaming flags of methods.pull/15817/head
parent
4367d72063
commit
54d7218431
14 changed files with 996 additions and 1 deletions
@ -0,0 +1,114 @@ |
|||||||
|
# Protocol Buffers - Google's data interchange format |
||||||
|
# Copyright 2024 Google Inc. All rights reserved. |
||||||
|
# |
||||||
|
# Use of this source code is governed by a BSD-style |
||||||
|
# license that can be found in the LICENSE file or at |
||||||
|
# https://developers.google.com/open-source/licenses/bsd |
||||||
|
|
||||||
|
module Google |
||||||
|
module Protobuf |
||||||
|
class MethodDescriptor |
||||||
|
attr :method_def, :descriptor_pool |
||||||
|
|
||||||
|
include Google::Protobuf::Internal::Convert |
||||||
|
|
||||||
|
# FFI Interface methods and setup |
||||||
|
extend ::FFI::DataConverter |
||||||
|
native_type ::FFI::Type::POINTER |
||||||
|
|
||||||
|
class << self |
||||||
|
prepend Google::Protobuf::Internal::TypeSafety |
||||||
|
include Google::Protobuf::Internal::PointerHelper |
||||||
|
|
||||||
|
# @param value [MethodDescriptor] MethodDescriptor to convert to an FFI native type |
||||||
|
# @param _ [Object] Unused |
||||||
|
def to_native(value, _) |
||||||
|
method_def_ptr = value.nil? ? nil : value.instance_variable_get(:@method_def) |
||||||
|
return ::FFI::Pointer::NULL if method_def_ptr.nil? |
||||||
|
raise "Underlying method_def was null!" if method_def_ptr.null? |
||||||
|
method_def_ptr |
||||||
|
end |
||||||
|
|
||||||
|
## |
||||||
|
# @param service_def [::FFI::Pointer] MethodDef pointer to be wrapped |
||||||
|
# @param _ [Object] Unused |
||||||
|
def from_native(method_def, _ = nil) |
||||||
|
return nil if method_def.nil? or method_def.null? |
||||||
|
service_def = Google::Protobuf::FFI.raw_service_def_by_raw_method_def(method_def) |
||||||
|
file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def) |
||||||
|
descriptor_from_file_def(file_def, method_def) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def self.new(*arguments, &block) |
||||||
|
raise "Descriptor objects may not be created from Ruby." |
||||||
|
end |
||||||
|
|
||||||
|
def to_s |
||||||
|
inspect |
||||||
|
end |
||||||
|
|
||||||
|
def inspect |
||||||
|
"#{self.class.name}: #{name}" |
||||||
|
end |
||||||
|
|
||||||
|
def name |
||||||
|
@name ||= Google::Protobuf::FFI.get_method_name(self) |
||||||
|
end |
||||||
|
|
||||||
|
def options |
||||||
|
@options ||= begin |
||||||
|
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1) |
||||||
|
temporary_arena = Google::Protobuf::FFI.create_arena |
||||||
|
buffer = Google::Protobuf::FFI.method_options(self, size_ptr, temporary_arena) |
||||||
|
Google::Protobuf::MethodOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def input_type |
||||||
|
@input_type ||= Google::Protobuf::FFI.method_input_type(self) |
||||||
|
end |
||||||
|
|
||||||
|
def output_type |
||||||
|
@output_type ||= Google::Protobuf::FFI.method_output_type(self) |
||||||
|
end |
||||||
|
|
||||||
|
def client_streaming |
||||||
|
@client_streaming ||= Google::Protobuf::FFI.method_client_streaming(self) |
||||||
|
end |
||||||
|
|
||||||
|
def server_streaming |
||||||
|
@server_streaming ||= Google::Protobuf::FFI.method_server_streaming(self) |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def initialize(method_def, descriptor_pool) |
||||||
|
@method_def = method_def |
||||||
|
@descriptor_pool = descriptor_pool |
||||||
|
end |
||||||
|
|
||||||
|
def self.private_constructor(method_def, descriptor_pool) |
||||||
|
instance = allocate |
||||||
|
instance.send(:initialize, method_def, descriptor_pool) |
||||||
|
instance |
||||||
|
end |
||||||
|
|
||||||
|
def c_type |
||||||
|
@c_type ||= Google::Protobuf::FFI.get_c_type(self) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
class FFI |
||||||
|
# MethodDef |
||||||
|
attach_function :raw_service_def_by_raw_method_def, :upb_MethodDef_Service, [:pointer], :pointer |
||||||
|
attach_function :get_method_name, :upb_MethodDef_Name, [MethodDescriptor], :string |
||||||
|
attach_function :method_options, :MethodDescriptor_serialized_options, [MethodDescriptor, :pointer, Internal::Arena], :pointer |
||||||
|
attach_function :method_input_type, :upb_MethodDef_InputType, [MethodDescriptor], Descriptor |
||||||
|
attach_function :method_output_type, :upb_MethodDef_OutputType, [MethodDescriptor], Descriptor |
||||||
|
attach_function :method_client_streaming, :upb_MethodDef_ClientStreaming, [MethodDescriptor], :bool |
||||||
|
attach_function :method_server_streaming, :upb_MethodDef_ServerStreaming, [MethodDescriptor], :bool |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
@ -0,0 +1,107 @@ |
|||||||
|
# Protocol Buffers - Google's data interchange format |
||||||
|
# Copyright 2024 Google Inc. All rights reserved. |
||||||
|
# |
||||||
|
# Use of this source code is governed by a BSD-style |
||||||
|
# license that can be found in the LICENSE file or at |
||||||
|
# https://developers.google.com/open-source/licenses/bsd |
||||||
|
|
||||||
|
module Google |
||||||
|
module Protobuf |
||||||
|
class ServiceDescriptor |
||||||
|
attr :service_def, :descriptor_pool |
||||||
|
include Enumerable |
||||||
|
|
||||||
|
include Google::Protobuf::Internal::Convert |
||||||
|
|
||||||
|
# FFI Interface methods and setup |
||||||
|
extend ::FFI::DataConverter |
||||||
|
native_type ::FFI::Type::POINTER |
||||||
|
|
||||||
|
class << self |
||||||
|
prepend Google::Protobuf::Internal::TypeSafety |
||||||
|
include Google::Protobuf::Internal::PointerHelper |
||||||
|
|
||||||
|
# @param value [ServiceDescriptor] ServiceDescriptor to convert to an FFI native type |
||||||
|
# @param _ [Object] Unused |
||||||
|
def to_native(value, _) |
||||||
|
service_def_ptr = value.nil? ? nil : value.instance_variable_get(:@service_def) |
||||||
|
return ::FFI::Pointer::NULL if service_def_ptr.nil? |
||||||
|
raise "Underlying service_def was null!" if service_def_ptr.null? |
||||||
|
service_def_ptr |
||||||
|
end |
||||||
|
|
||||||
|
## |
||||||
|
# @param service_def [::FFI::Pointer] ServiceDef pointer to be wrapped |
||||||
|
# @param _ [Object] Unused |
||||||
|
def from_native(service_def, _ = nil) |
||||||
|
return nil if service_def.nil? or service_def.null? |
||||||
|
file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def) |
||||||
|
descriptor_from_file_def(file_def, service_def) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def self.new(*arguments, &block) |
||||||
|
raise "Descriptor objects may not be created from Ruby." |
||||||
|
end |
||||||
|
|
||||||
|
def to_s |
||||||
|
inspect |
||||||
|
end |
||||||
|
|
||||||
|
def inspect |
||||||
|
"#{self.class.name}: #{name}" |
||||||
|
end |
||||||
|
|
||||||
|
def name |
||||||
|
@name ||= Google::Protobuf::FFI.get_service_full_name(self) |
||||||
|
end |
||||||
|
|
||||||
|
def file_descriptor |
||||||
|
@descriptor_pool.send(:get_file_descriptor, Google::Protobuf::FFI.file_def_by_raw_service_def(@service_def)) |
||||||
|
end |
||||||
|
|
||||||
|
def each &block |
||||||
|
n = Google::Protobuf::FFI.method_count(self) |
||||||
|
0.upto(n-1) do |i| |
||||||
|
yield(Google::Protobuf::FFI.get_method_by_index(self, i)) |
||||||
|
end |
||||||
|
nil |
||||||
|
end |
||||||
|
|
||||||
|
def options |
||||||
|
@options ||= begin |
||||||
|
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1) |
||||||
|
temporary_arena = Google::Protobuf::FFI.create_arena |
||||||
|
buffer = Google::Protobuf::FFI.service_options(self, size_ptr, temporary_arena) |
||||||
|
Google::Protobuf::ServiceOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def initialize(service_def, descriptor_pool) |
||||||
|
@service_def = service_def |
||||||
|
@descriptor_pool = descriptor_pool |
||||||
|
end |
||||||
|
|
||||||
|
def self.private_constructor(service_def, descriptor_pool) |
||||||
|
instance = allocate |
||||||
|
instance.send(:initialize, service_def, descriptor_pool) |
||||||
|
instance |
||||||
|
end |
||||||
|
|
||||||
|
def c_type |
||||||
|
@c_type ||= Google::Protobuf::FFI.get_c_type(self) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
class FFI |
||||||
|
# ServiceDef |
||||||
|
attach_function :file_def_by_raw_service_def, :upb_ServiceDef_File, [:pointer], :FileDef |
||||||
|
attach_function :get_service_full_name, :upb_ServiceDef_FullName, [ServiceDescriptor], :string |
||||||
|
attach_function :method_count, :upb_ServiceDef_MethodCount, [ServiceDescriptor], :int |
||||||
|
attach_function :get_method_by_index, :upb_ServiceDef_Method, [ServiceDescriptor, :int], MethodDescriptor |
||||||
|
attach_function :service_options, :ServiceDescriptor_serialized_options, [ServiceDescriptor, :pointer, Internal::Arena], :pointer |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,156 @@ |
|||||||
|
/* |
||||||
|
* Protocol Buffers - Google's data interchange format |
||||||
|
* Copyright 2024 Google Inc. All rights reserved. |
||||||
|
* https://developers.google.com/protocol-buffers/
|
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.google.protobuf.jruby; |
||||||
|
|
||||||
|
|
||||||
|
import com.google.protobuf.CodedInputStream; |
||||||
|
import com.google.protobuf.Descriptors.MethodDescriptor; |
||||||
|
import org.jruby.*; |
||||||
|
import org.jruby.anno.JRubyClass; |
||||||
|
import org.jruby.anno.JRubyMethod; |
||||||
|
import org.jruby.runtime.Block; |
||||||
|
import org.jruby.runtime.ObjectAllocator; |
||||||
|
import org.jruby.runtime.ThreadContext; |
||||||
|
import org.jruby.runtime.builtin.IRubyObject; |
||||||
|
import org.jruby.anno.JRubyClass; |
||||||
|
import org.jruby.anno.JRubyMethod; |
||||||
|
|
||||||
|
@JRubyClass(name = "MethoDescriptor") |
||||||
|
public class RubyMethodDescriptor extends RubyObject { |
||||||
|
public static void createRubyMethodDescriptor(Ruby runtime) { |
||||||
|
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); |
||||||
|
RubyClass cMethodDescriptor = |
||||||
|
protobuf.defineClassUnder( |
||||||
|
"MethodDescriptor", |
||||||
|
runtime.getObject(), |
||||||
|
new ObjectAllocator() { |
||||||
|
@Override |
||||||
|
public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
||||||
|
return new RubyMethodDescriptor(runtime, klazz); |
||||||
|
} |
||||||
|
}); |
||||||
|
cMethodDescriptor.defineAnnotatedMethods(RubyMethodDescriptor.class); |
||||||
|
} |
||||||
|
|
||||||
|
public RubyMethodDescriptor(Ruby runtime, RubyClass klazz) { |
||||||
|
super(runtime, klazz); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* MethodDescriptor.name => name |
||||||
|
* |
||||||
|
* Returns the name of this method |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "name") |
||||||
|
public IRubyObject getName(ThreadContext context) { |
||||||
|
return context.runtime.newString(this.descriptor.getName()); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* MethodDescriptor.options |
||||||
|
* |
||||||
|
* Returns the options set on this protobuf rpc method |
||||||
|
*/ |
||||||
|
@JRubyMethod |
||||||
|
public IRubyObject options(ThreadContext context) { |
||||||
|
RubyDescriptorPool pool = (RubyDescriptorPool) RubyDescriptorPool.generatedPool(null, null); |
||||||
|
RubyDescriptor methodOptionsDescriptor = |
||||||
|
(RubyDescriptor) |
||||||
|
pool.lookup(context, context.runtime.newString("google.protobuf.MethodOptions")); |
||||||
|
RubyClass methodOptionsClass = (RubyClass) methodOptionsDescriptor.msgclass(context); |
||||||
|
RubyMessage msg = (RubyMessage) methodOptionsClass.newInstance(context, Block.NULL_BLOCK); |
||||||
|
return msg.decodeBytes( |
||||||
|
context, |
||||||
|
msg, |
||||||
|
CodedInputStream.newInstance( |
||||||
|
descriptor.getOptions().toByteString().toByteArray()), /*freeze*/ |
||||||
|
true); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* MethodDescriptor.input_type => Descriptor |
||||||
|
* |
||||||
|
* Returns the `Descriptor` for the request message type of this method |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "input_type") |
||||||
|
public IRubyObject getInputType(ThreadContext context) { |
||||||
|
return this.pool.lookup(context, context.runtime.newString(this.descriptor.getInputType().getFullName())); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* MethodDescriptor.output_type => Descriptor |
||||||
|
* |
||||||
|
* Returns the `Descriptor` for the response message type of this method |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "output_type") |
||||||
|
public IRubyObject getOutputType(ThreadContext context) { |
||||||
|
return this.pool.lookup(context, context.runtime.newString(this.descriptor.getOutputType().getFullName())); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* MethodDescriptor.client_streaming => bool |
||||||
|
* |
||||||
|
* Returns whether or not this is a streaming request method |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "client_streaming") |
||||||
|
public IRubyObject getClientStreaming(ThreadContext context) { |
||||||
|
return this.descriptor.isClientStreaming() ? context.runtime.getTrue() : context.runtime.getFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* MethodDescriptor.server_streaming => bool |
||||||
|
* |
||||||
|
* Returns whether or not this is a streaming response method |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "server_streaming") |
||||||
|
public IRubyObject getServerStreaming(ThreadContext context) { |
||||||
|
return this.descriptor.isServerStreaming() ? context.runtime.getTrue() : context.runtime.getFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void setDescriptor( |
||||||
|
ThreadContext context, MethodDescriptor descriptor, RubyDescriptorPool pool) { |
||||||
|
this.descriptor = descriptor; |
||||||
|
this.pool = pool; |
||||||
|
} |
||||||
|
|
||||||
|
private MethodDescriptor descriptor; |
||||||
|
private IRubyObject name; |
||||||
|
private RubyDescriptorPool pool; |
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
/* |
||||||
|
* Protocol Buffers - Google's data interchange format |
||||||
|
* Copyright 2024 Google Inc. All rights reserved. |
||||||
|
* https://developers.google.com/protocol-buffers/
|
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.google.protobuf.jruby; |
||||||
|
|
||||||
|
|
||||||
|
import com.google.protobuf.CodedInputStream; |
||||||
|
import com.google.protobuf.Descriptors.MethodDescriptor; |
||||||
|
import com.google.protobuf.Descriptors.ServiceDescriptor; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
import org.jruby.*; |
||||||
|
import org.jruby.anno.JRubyClass; |
||||||
|
import org.jruby.anno.JRubyMethod; |
||||||
|
import org.jruby.runtime.Block; |
||||||
|
import org.jruby.runtime.ObjectAllocator; |
||||||
|
import org.jruby.runtime.ThreadContext; |
||||||
|
import org.jruby.runtime.builtin.IRubyObject; |
||||||
|
import org.jruby.anno.JRubyClass; |
||||||
|
import org.jruby.anno.JRubyMethod; |
||||||
|
|
||||||
|
@JRubyClass(name = "ServiceDescriptor") |
||||||
|
public class RubyServiceDescriptor extends RubyObject { |
||||||
|
public static void createRubyServiceDescriptor(Ruby runtime) { |
||||||
|
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); |
||||||
|
RubyClass cServiceDescriptor = |
||||||
|
protobuf.defineClassUnder( |
||||||
|
"ServiceDescriptor", |
||||||
|
runtime.getObject(), |
||||||
|
new ObjectAllocator() { |
||||||
|
@Override |
||||||
|
public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
||||||
|
return new RubyServiceDescriptor(runtime, klazz); |
||||||
|
} |
||||||
|
}); |
||||||
|
cServiceDescriptor.includeModule(runtime.getEnumerable()); |
||||||
|
cServiceDescriptor.defineAnnotatedMethods(RubyServiceDescriptor.class); |
||||||
|
cMethodDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::MethodDescriptor"); |
||||||
|
} |
||||||
|
|
||||||
|
public RubyServiceDescriptor(Ruby runtime, RubyClass klazz) { |
||||||
|
super(runtime, klazz); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* ServiceDescriptor.name => name |
||||||
|
* |
||||||
|
* Returns the name of this service type as a fully-qualified string (e.g., |
||||||
|
* My.Package.Service). |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "name") |
||||||
|
public IRubyObject getName(ThreadContext context) { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* ServiceDescriptor.file_descriptor |
||||||
|
* |
||||||
|
* Returns the FileDescriptor object this service belongs to. |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "file_descriptor") |
||||||
|
public IRubyObject getFileDescriptor(ThreadContext context) { |
||||||
|
return RubyFileDescriptor.getRubyFileDescriptor(context, descriptor); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* ServiceDescriptor.options |
||||||
|
* |
||||||
|
* Returns the options set on this protobuf service |
||||||
|
*/ |
||||||
|
@JRubyMethod |
||||||
|
public IRubyObject options(ThreadContext context) { |
||||||
|
RubyDescriptorPool pool = (RubyDescriptorPool) RubyDescriptorPool.generatedPool(null, null); |
||||||
|
RubyDescriptor serviceOptionsDescriptor = |
||||||
|
(RubyDescriptor) |
||||||
|
pool.lookup(context, context.runtime.newString("google.protobuf.ServiceOptions")); |
||||||
|
RubyClass serviceOptionsClass = (RubyClass) serviceOptionsDescriptor.msgclass(context); |
||||||
|
RubyMessage msg = (RubyMessage) serviceOptionsClass.newInstance(context, Block.NULL_BLOCK); |
||||||
|
return msg.decodeBytes( |
||||||
|
context, |
||||||
|
msg, |
||||||
|
CodedInputStream.newInstance( |
||||||
|
descriptor.getOptions().toByteString().toByteArray()), /*freeze*/ |
||||||
|
true); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* call-seq: |
||||||
|
* ServiceDescriptor.each(&block) |
||||||
|
* |
||||||
|
* Iterates over methods in this service, yielding to the block on each one. |
||||||
|
*/ |
||||||
|
@JRubyMethod(name = "each") |
||||||
|
public IRubyObject each(ThreadContext context, Block block) { |
||||||
|
for (Map.Entry<IRubyObject, RubyMethodDescriptor> entry : methodDescriptors.entrySet()) { |
||||||
|
block.yield(context, entry.getValue()); |
||||||
|
} |
||||||
|
return context.nil; |
||||||
|
} |
||||||
|
|
||||||
|
protected void setDescriptor( |
||||||
|
ThreadContext context, ServiceDescriptor descriptor, RubyDescriptorPool pool) { |
||||||
|
this.descriptor = descriptor; |
||||||
|
|
||||||
|
// Populate the methods (and preserve the order by using LinkedHashMap)
|
||||||
|
methodDescriptors = new LinkedHashMap<IRubyObject, RubyMethodDescriptor>(); |
||||||
|
|
||||||
|
for (MethodDescriptor methodDescriptor : descriptor.getMethods()) { |
||||||
|
RubyMethodDescriptor md = |
||||||
|
(RubyMethodDescriptor) cMethodDescriptor.newInstance(context, Block.NULL_BLOCK); |
||||||
|
md.setDescriptor(context, methodDescriptor, pool); |
||||||
|
methodDescriptors.put(context.runtime.newString(methodDescriptor.getName()), md); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void setName(IRubyObject name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
private static RubyClass cMethodDescriptor; |
||||||
|
|
||||||
|
private ServiceDescriptor descriptor; |
||||||
|
private Map<IRubyObject, RubyMethodDescriptor> methodDescriptors; |
||||||
|
private IRubyObject name; |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
syntax = "proto3"; |
||||||
|
import "google/protobuf/descriptor.proto"; |
||||||
|
|
||||||
|
package service_test_protos; |
||||||
|
|
||||||
|
message UnaryRequestType { |
||||||
|
string ping = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message UnaryResponseType { |
||||||
|
string pong = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message StreamRequestType { |
||||||
|
string ping = 1; |
||||||
|
uint32 sequence = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message StreamResponseType { |
||||||
|
string pong = 1; |
||||||
|
uint32 sequence = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message TestOptionsType { |
||||||
|
uint32 int_option_value = 1; |
||||||
|
} |
||||||
|
|
||||||
|
extend google.protobuf.ServiceOptions { |
||||||
|
optional TestOptionsType test_options = 50000; |
||||||
|
} |
||||||
|
|
||||||
|
service TestService { |
||||||
|
option(test_options).int_option_value = 8325; |
||||||
|
|
||||||
|
rpc UnaryOne(UnaryRequestType) returns (UnaryResponseType); |
||||||
|
rpc UnaryTwo(UnaryRequestType) returns (UnaryResponseType); |
||||||
|
|
||||||
|
rpc IdempotentMethod(UnaryRequestType) returns (UnaryResponseType) { |
||||||
|
option idempotency_level = IDEMPOTENT; |
||||||
|
} |
||||||
|
rpc PureMethod(UnaryRequestType) returns (UnaryResponseType) { |
||||||
|
option idempotency_level = NO_SIDE_EFFECTS; |
||||||
|
} |
||||||
|
|
||||||
|
rpc StreamingMethod(stream StreamRequestType) returns (stream StreamResponseType); |
||||||
|
} |
||||||
|
|
||||||
|
service DeprecatedService { |
||||||
|
option deprecated = true; |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
#!/usr/bin/ruby |
||||||
|
|
||||||
|
require 'google/protobuf' |
||||||
|
require 'service_test_pb' |
||||||
|
require 'test/unit' |
||||||
|
require 'json' |
||||||
|
|
||||||
|
class ServiceTest < Test::Unit::TestCase |
||||||
|
def setup |
||||||
|
@test_service = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.TestService') |
||||||
|
@deprecated_service = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.DeprecatedService') |
||||||
|
@test_service_methods = @test_service.to_h { |method| [method.name, method] } |
||||||
|
end |
||||||
|
|
||||||
|
def test_lookup_service_descriptor |
||||||
|
assert_kind_of Google::Protobuf::ServiceDescriptor, @test_service |
||||||
|
assert_equal 'service_test_protos.TestService', @test_service.name |
||||||
|
end |
||||||
|
|
||||||
|
def test_file_descriptor |
||||||
|
assert_kind_of Google::Protobuf::FileDescriptor, @test_service.file_descriptor |
||||||
|
assert_equal 'service_test.proto', @test_service.file_descriptor.name |
||||||
|
end |
||||||
|
|
||||||
|
def test_method_iteration |
||||||
|
@test_service.each { |method| assert_kind_of Google::Protobuf::MethodDescriptor, method } |
||||||
|
assert_equal %w(UnaryOne UnaryTwo), @test_service.map { |method| method.name }.first(2) |
||||||
|
end |
||||||
|
|
||||||
|
def test_service_options |
||||||
|
assert @deprecated_service.options.deprecated |
||||||
|
refute @test_service.options.deprecated |
||||||
|
end |
||||||
|
|
||||||
|
def test_service_options_extensions |
||||||
|
extension_field = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.test_options') |
||||||
|
assert_equal 8325, extension_field.get(@test_service.options).int_option_value |
||||||
|
end |
||||||
|
|
||||||
|
def test_method_options |
||||||
|
assert_equal :IDEMPOTENT, @test_service_methods['IdempotentMethod'].options.idempotency_level |
||||||
|
assert_equal :NO_SIDE_EFFECTS, @test_service_methods['PureMethod'].options.idempotency_level |
||||||
|
end |
||||||
|
|
||||||
|
def test_method_input_type |
||||||
|
unary_request_type = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.UnaryRequestType') |
||||||
|
assert_same unary_request_type, @test_service_methods['UnaryOne'].input_type |
||||||
|
end |
||||||
|
|
||||||
|
def test_method_output_type |
||||||
|
unary_response_type = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.UnaryResponseType') |
||||||
|
assert_same unary_response_type, @test_service_methods['UnaryOne'].output_type |
||||||
|
end |
||||||
|
|
||||||
|
def test_method_streaming_flags |
||||||
|
refute @test_service_methods['UnaryOne'].client_streaming |
||||||
|
refute @test_service_methods['UnaryOne'].server_streaming |
||||||
|
assert @test_service_methods['StreamingMethod'].client_streaming |
||||||
|
assert @test_service_methods['StreamingMethod'].server_streaming |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue