require 'google/ads/google_ads/utils/string_utils' module Google module Ads module GoogleAds class ServiceWrapper # @param service a generated gax client object # @param rpc_inputs [Hash] a hash from rpc method names on the passed # service to their input types, e.g. for GoogleAdsService::Client a # hash containing {search: SearchGoogleAdsRequest} etc. # def initialize(service:, rpc_inputs:, deprecation:) @service = service @rpc_inputs = rpc_inputs @deprecation = deprecation end def configure(&block) @service.configure(&block) end def respond_to_missing?(m, include_private=false) service.respond_to?(m, include_private) end def method_missing(name, *args, **kwargs, &blk) # delegate with no request manipulation if the method isn't an # rpc, so that we don't elide any of the configuration or other # methods available on the gax clients unless rpc_inputs.include?(name) return service.public_send(name, *args, **kwargs, &blk) end rpc_input_class = rpc_inputs.fetch(name) request = rpc_input_class.new if kwargs.include?(:request) || kwargs.include?(:options) options = kwargs.delete(:options) kwargs.fetch(:request, {}).each do |name, value| write_field(request, value, name) end service.public_send(name, request, options) elsif !kwargs.empty? && args.empty? kwargs.each do |name, value| write_field(request, value, name) end service.public_send(name, request, nil) elsif args.empty? # no args specified at all, just pass through service.public_send(name, request, nil) else # this branch is the legacy version, which is no longer supported # zip will fill with nils by default, so we truncate the number # of fields we set to match the argument length here. args_and_fields = args.zip( request_positional_args(rpc_input_class)[0...args.length] ) field_names = [] args_and_fields.each do |(arg, field)| field_names << field.name end service_name_segments = service.class.name.split("::") service_name = GoogleAds::Utils.underscore( service_name_segments[service_name_segments.length-2] ) method_name = "#{service_name}.#{name}" raise ArgumentError, "Calling #{method_name} with positional " \ "arguments is deprecated, please update it to use kwargs, e.g.: " \ "#{method_name}" \ "(#{field_names.map { |fn| "#{fn}: ..."}.join(", ")})" end end private def request_positional_args(rpc_input_class) # one of fields are never positional arguments in the old monolithic # generator's code, so this selects all the one of fields in the # descriptor and puts their names in a set. Then we remove them # from the final array of args we return. one_of_fields = Set.new rpc_input_class.descriptor.each_oneof do |one_of| one_of.each do |field| one_of_fields.add(field.name) end end fields_to_reject = one_of_fields fields_to_reject.add("partial_failure") fields_to_reject.add("validate_only") rpc_input_class.descriptor.to_a.reject { |field| fields_to_reject.include?(field.name) } end def write_field(request, arg, field) if Symbol === field name = field.to_s field = request.class.descriptor.lookup(field.to_s) raise ArgumentError, "unknown field #{name}" if field.nil? end if field.label == :repeated unless Array === arg raise( ArgumentError, "Must pass an array to assign to repeated field #{field.name}", ) end repeated = request.public_send(field.name) arg.each { |a| repeated << a } else request.public_send("#{field.name}=", arg) end end attr_reader :service attr_reader :rpc_inputs attr_reader :deprecation end end end end