# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "grpc" require "googleauth" require "gapic/grpc/service_stub/rpc_call" require "gapic/grpc/service_stub/channel" require "gapic/grpc/service_stub/channel_pool" require "gapic/logging_concerns" require "gapic/universe_domain_concerns" module Gapic ## # Gapic gRPC Stub # # This class wraps the actual gRPC Stub and ChannelPool objects. # # @!attribute [r] grpc_stub # @return [Object] The instance of the gRPC stub class (`grpc_stub_class`) constructor argument. # @!attribute [r] channel_pool # @return [Gapic::ServiceStub::ChannelPool] The instance of the ChannelPool class. # class ServiceStub include UniverseDomainConcerns include LoggingConcerns attr_reader :grpc_stub attr_reader :channel_pool ## # Creates a Gapic gRPC stub object. # # @param grpc_stub_class [Class] gRPC stub class to create a new instance of. # @param endpoint [String] The endpoint of the API. Overrides any endpoint_template. # @param endpoint_template [String] The endpoint of the API, where the # universe domain component of the hostname is marked by the string in # the constant {UniverseDomainConcerns::ENDPOINT_SUBSTITUTION}. # @param universe_domain [String] The universe domain in which calls should # be made. Defaults to `googleapis.com`. # @param credentials [Google::Auth::Credentials, Signet::OAuth2::Client, String, Hash, Proc, # ::GRPC::Core::Channel, ::GRPC::Core::ChannelCredentials] Provides the means for authenticating requests made by # the client. This parameter can be many types: # # * A `Google::Auth::Credentials` uses a the properties of its represented keyfile for authenticating requests # made by this client. # * A `Signet::OAuth2::Client` object used to apply the OAuth credentials. # * A `::GRPC::Core::Channel` will be used to make calls through. # * A `::GRPC::Core::ChannelCredentials` for the setting up the RPC client. The channel credentials should # already be composed with a `::GRPC::Core::CallCredentials` object. # * A `Proc` will be used as an updater_proc for the Grpc channel. The proc transforms the metadata for # requests, generally, to give OAuth credentials. # @param channel_args [Hash] The channel arguments. (This argument is ignored when `credentials` is # provided as a `::GRPC::Core::Channel`.) # @param interceptors [Array<::GRPC::ClientInterceptor>] An array of {::GRPC::ClientInterceptor} objects that will # be used for intercepting calls before they are executed Interceptors are an EXPERIMENTAL API. # @param channel_pool_config [::Gapic::ServiceStub:ChannelPool::Configuration] The configuration for channel # pool. This argument will raise error when `credentials` is provided as a `::GRPC::Core::Channel`. # @param logger [Logger,:default,nil] An explicit logger to use, or one # of the values `:default` (the default) to construct a default logger, # or `nil` to disable logging explicitly. # def initialize grpc_stub_class, credentials:, endpoint: nil, endpoint_template: nil, universe_domain: nil, channel_args: nil, interceptors: nil, channel_pool_config: nil, logger: :default raise ArgumentError, "grpc_stub_class is required" if grpc_stub_class.nil? setup_universe_domain universe_domain: universe_domain, endpoint: endpoint, endpoint_template: endpoint_template, credentials: credentials setup_logging logger: logger, system_name: grpc_stub_class, service: grpc_stub_class, endpoint: self.endpoint, client_id: object_id @channel_pool = nil @grpc_stub = nil channel_args = Hash channel_args interceptors = Array interceptors if channel_pool_config && channel_pool_config.channel_count > 1 create_channel_pool grpc_stub_class, endpoint: self.endpoint, credentials: self.credentials, channel_args: channel_args, interceptors: interceptors, channel_pool_config: channel_pool_config else create_grpc_stub grpc_stub_class, endpoint: self.endpoint, credentials: self.credentials, channel_args: channel_args, interceptors: interceptors end end def create_channel_pool grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil, channel_pool_config: nil if credentials.is_a? ::GRPC::Core::Channel raise ArgumentError, "Cannot create a channel pool with GRPC::Core::Channel as credentials" end @channel_pool = ChannelPool.new grpc_stub_class, endpoint: endpoint, credentials: credentials, channel_args: channel_args, interceptors: interceptors, config: channel_pool_config, stub_logger: stub_logger end def create_grpc_stub grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil @grpc_stub = case credentials when ::GRPC::Core::Channel grpc_stub_class.new endpoint, nil, channel_override: credentials, interceptors: interceptors when ::GRPC::Core::ChannelCredentials, Symbol grpc_stub_class.new endpoint, credentials, channel_args: channel_args, interceptors: interceptors else updater_proc = credentials.updater_proc if credentials.respond_to? :updater_proc updater_proc ||= credentials if credentials.is_a? Proc raise ArgumentError, "invalid credentials (#{credentials.class})" if updater_proc.nil? call_creds = ::GRPC::Core::CallCredentials.new updater_proc chan_creds = ::GRPC::Core::ChannelCredentials.new.compose call_creds grpc_stub_class.new endpoint, chan_creds, channel_args: channel_args, interceptors: interceptors end end ## # Invoke the specified RPC call. # # @param method_name [Symbol] The RPC method name. # @param request [Object] The request object. # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided to # customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object # should only be used once. # # @yield [response, operation] Access the response along with the RPC operation. # @yieldparam response [Object] The response object. # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response. # # @return [Object] The response object. # # @example # require "google/showcase/v1beta1/echo_pb" # require "google/showcase/v1beta1/echo_services_pb" # require "gapic" # require "gapic/grpc" # # echo_channel = ::GRPC::Core::Channel.new( # "localhost:7469", nil, :this_channel_is_insecure # ) # echo_stub = Gapic::ServiceStub.new( # Google::Showcase::V1beta1::Echo::Stub, # endpoint: "localhost:7469", credentials: echo_channel # ) # # request = Google::Showcase::V1beta1::EchoRequest.new # response = echo_stub.call_rpc :echo, request # # @example Using custom call options: # require "google/showcase/v1beta1/echo_pb" # require "google/showcase/v1beta1/echo_services_pb" # require "gapic" # require "gapic/grpc" # # echo_channel = ::GRPC::Core::Channel.new( # "localhost:7469", nil, :this_channel_is_insecure # ) # echo_stub = Gapic::ServiceStub.new( # Google::Showcase::V1beta1::Echo::Stub, # endpoint: "localhost:7469", credentials: echo_channel # ) # # request = Google::Showcase::V1beta1::EchoRequest.new # options = Gapic::CallOptions.new( # retry_policy = { # retry_codes: [::GRPC::Core::StatusCodes::UNAVAILABLE] # } # ) # response = echo_stub.call_rpc :echo, request # options: options # # @example Accessing the response and RPC operation using a block: # require "google/showcase/v1beta1/echo_pb" # require "google/showcase/v1beta1/echo_services_pb" # require "gapic" # require "gapic/grpc" # # echo_channel = ::GRPC::Core::Channel.new( # "localhost:7469", nil, :this_channel_is_insecure # ) # echo_stub = Gapic::ServiceStub.new( # Google::Showcase::V1beta1::Echo::Stub, # endpoint: "localhost:7469", credentials: echo_channel # ) # # request = Google::Showcase::V1beta1::EchoRequest.new # echo_stub.call_rpc :echo, request do |response, operation| # operation.trailing_metadata # end # def call_rpc method_name, request, options: nil, &block if @channel_pool.nil? meth = @grpc_stub.method method_name rpc_call = RpcCall.new meth, stub_logger: stub_logger, method_name: method_name rpc_call.call request, options: options, &block else @channel_pool.call_rpc method_name, request, options: options, &block end end end end