diff --git a/codegen/templates/services.rb.erb b/codegen/templates/services.rb.erb index 53017d35a..2a611c6d0 100644 --- a/codegen/templates/services.rb.erb +++ b/codegen/templates/services.rb.erb @@ -9,6 +9,7 @@ module Google def initialize( logging_interceptor:, error_interceptor:, + metadata_interceptor:, credentials:, metadata:, endpoint:, @@ -17,6 +18,7 @@ module Google @interceptors = [ error_interceptor, logging_interceptor, + metadata_interceptor ].compact @credentials = credentials @metadata = metadata diff --git a/lib/google/ads/google_ads/config.rb b/lib/google/ads/google_ads/config.rb index 105099e8a..5838f807d 100644 --- a/lib/google/ads/google_ads/config.rb +++ b/lib/google/ads/google_ads/config.rb @@ -34,6 +34,7 @@ class Config attr_accessor :login_customer_id attr_accessor :linked_customer_id attr_accessor :use_cloud_org_for_api_access + attr_accessor :gaada attr_accessor :log_level attr_accessor :log_target @@ -59,6 +60,7 @@ def initialize(&block) @login_customer_id = nil @linked_customer_id = nil @use_cloud_org_for_api_access = false + @gaada = nil @log_level = nil @log_target = nil diff --git a/lib/google/ads/google_ads/google_ads_client.rb b/lib/google/ads/google_ads/google_ads_client.rb index 75347a8b1..898da9c92 100644 --- a/lib/google/ads/google_ads/google_ads_client.rb +++ b/lib/google/ads/google_ads/google_ads_client.rb @@ -114,6 +114,7 @@ def load_environment_config if @config.use_cloud_org_for_api_access.is_a?(String) @config.use_cloud_org_for_api_access = @config.use_cloud_org_for_api_access.downcase == "true" end + @config.gaada = ENV.fetch("GOOGLE_ADS_GAADA", @config.gaada) end # Return a service for the provided entity type. For example, passing diff --git a/lib/google/ads/google_ads/interceptors/metadata_interceptor.rb b/lib/google/ads/google_ads/interceptors/metadata_interceptor.rb new file mode 100644 index 000000000..799ee2d90 --- /dev/null +++ b/lib/google/ads/google_ads/interceptors/metadata_interceptor.rb @@ -0,0 +1,78 @@ +# Encoding: utf-8 +# +# Copyright 2022 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. +# +# Interceptor to add metadata headers to requests. + +require 'grpc/generic/interceptors' +require 'google/protobuf' + +module Google + module Ads + module GoogleAds + module Interceptors + class MetadataInterceptor < GRPC::ClientInterceptor + def initialize(developer_token, login_customer_id, linked_customer_id, use_cloud_org_for_api_access, gaada) + super() + @developer_token = developer_token + @login_customer_id = login_customer_id + @linked_customer_id = linked_customer_id + @use_cloud_org_for_api_access = use_cloud_org_for_api_access + @gaada = gaada + end + + def request_response(request:, call:, method:, metadata: {}) + update_metadata(metadata) + yield + end + + def server_streamer(request:, call:, method:, metadata: {}) + update_metadata(metadata) + yield + end + + private + + def update_metadata(metadata) + if !@use_cloud_org_for_api_access + metadata[:"developer-token"] = @developer_token + end + + if @login_customer_id + metadata[:"login-customer-id"] = @login_customer_id.to_s + end + + if @linked_customer_id + metadata[:"linked-customer-id"] = @linked_customer_id.to_s + end + + # The python library iterates over metadata and modifies x-goog-api-client + # Here we can directly access it. + if metadata.key?(:"x-goog-api-client") + if @gaada + metadata[:"x-goog-api-client"] += " gaada/#{@gaada}" + end + + # Check if "pb" is already in the header + unless metadata[:"x-goog-api-client"].include?("pb") + metadata[:"x-goog-api-client"] += " pb/#{Gem.loaded_specs["google-protobuf"].version}" + end + end + end + end + end + end + end +end diff --git a/lib/google/ads/google_ads/service_lookup.rb b/lib/google/ads/google_ads/service_lookup.rb index 095d703f0..e0d632df0 100644 --- a/lib/google/ads/google_ads/service_lookup.rb +++ b/lib/google/ads/google_ads/service_lookup.rb @@ -1,5 +1,6 @@ require 'google/ads/google_ads/interceptors/logging_interceptor' require 'google/ads/google_ads/interceptors/error_interceptor' +require 'google/ads/google_ads/interceptors/metadata_interceptor' module Google module Ads @@ -26,16 +27,24 @@ def call logging_interceptor = GoogleAds::Interceptors::LoggingInterceptor.new(logger) end error_interceptor = GoogleAds::Interceptors::ErrorInterceptor.new + metadata_interceptor = GoogleAds::Interceptors::MetadataInterceptor.new( + config.developer_token, + config.login_customer_id, + config.linked_customer_id, + config.use_cloud_org_for_api_access, + config.gaada + ) version_alternates = {} Factories::VERSIONS.each do |v| - version_alternates[v] = factory_at_version(v, error_interceptor, logging_interceptor) + version_alternates[v] = factory_at_version(v, error_interceptor, logging_interceptor, metadata_interceptor) end highest_factory = factory_at_version( Factories::HIGHEST_VERSION, error_interceptor, logging_interceptor, + metadata_interceptor, ) VersionAlternate.new(highest_factory, version_alternates) @@ -43,10 +52,11 @@ def call private - def factory_at_version(version, error_interceptor, logging_interceptor) + def factory_at_version(version, error_interceptor, logging_interceptor, metadata_interceptor) factory = Factories.at_version(version).services.new(**{ logging_interceptor: logging_interceptor, error_interceptor: error_interceptor, + metadata_interceptor: metadata_interceptor, deprecation: deprecator }.merge(gax_service_params)) @@ -62,27 +72,15 @@ def gax_service_params end def headers - headers = {} - - # If config.use_cloud_org_for_api_access is not True, add the developer - # token to the request's metadata - if !config.use_cloud_org_for_api_access - headers[:"developer-token"] = config.developer_token - end - if config.login_customer_id validate_customer_id(:login_customer_id) - # header values must be strings - headers[:"login-customer-id"] = config.login_customer_id.to_s end if config.linked_customer_id validate_customer_id(:linked_customer_id) - # header values must be strings - headers[:"linked-customer-id"] = config.linked_customer_id.to_s end - headers + {} end def validate_customer_id(field) diff --git a/test/test_config.rb b/test/test_config.rb index f8dd28c21..0cc3fa99d 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -27,18 +27,21 @@ def test_initialize() client_id_value = 'client id' client_secret_value = 'client_secret' developer_token_value = 'developer_token' + gaada_value = 'gaada_value' config = Google::Ads::GoogleAds::Config.new do |c| c.refresh_token = refresh_token_value c.client_id = client_id_value c.client_secret = client_secret_value c.developer_token = developer_token_value + c.gaada = gaada_value end assert_equal(refresh_token_value, config.refresh_token) assert_equal(client_id_value, config.client_id) assert_equal(client_secret_value, config.client_secret) assert_equal(developer_token_value, config.developer_token) + assert_equal(gaada_value, config.gaada) end def test_configure() @@ -48,18 +51,21 @@ def test_configure() client_id_value = 'abcd' client_secret_value = '!@#$' developer_token_value = '7x&Z' + gaada_value = 'gaada_value' config.configure do |c| c.refresh_token = refresh_token_value c.client_id = client_id_value c.client_secret = client_secret_value c.developer_token = developer_token_value + c.gaada = gaada_value end assert_equal(refresh_token_value, config.refresh_token) assert_equal(client_id_value, config.client_id) assert_equal(client_secret_value, config.client_secret) assert_equal(developer_token_value, config.developer_token) + assert_equal(gaada_value, config.gaada) end def test_use_cloud_org_for_api_access() diff --git a/test/test_metadata_interceptor.rb b/test/test_metadata_interceptor.rb new file mode 100644 index 000000000..5d6a596a5 --- /dev/null +++ b/test/test_metadata_interceptor.rb @@ -0,0 +1,108 @@ +#!/usr/bin/env ruby +# Encoding: utf-8 + +require 'minitest/autorun' +require 'google/ads/google_ads/interceptors/metadata_interceptor' +require 'google/protobuf' + +class TestMetadataInterceptor < Minitest::Test + attr_reader :mi + + def setup + @mi = Google::Ads::GoogleAds::Interceptors::MetadataInterceptor.new( + "dev_token", + "login_id", + "linked_id", + false, + nil + ) + end + + def test_adds_developer_token_if_not_cloud_org + metadata = {} + mi.request_response( + request: nil, + call: nil, + method: nil, + metadata: metadata + ) do + end + assert_equal "dev_token", metadata[:"developer-token"] + end + + def test_adds_login_and_linked_customer_id + metadata = {} + mi.request_response( + request: nil, + call: nil, + method: nil, + metadata: metadata + ) do + end + assert_equal "login_id", metadata[:"login-customer-id"] + assert_equal "linked_id", metadata[:"linked-customer-id"] + end + + def test_appends_pb_version_to_x_goog_api_client + metadata = { :"x-goog-api-client" => "gl-ruby/1.2.3" } + mi.request_response( + request: nil, + call: nil, + method: nil, + metadata: metadata + ) do + end + assert_includes metadata[:"x-goog-api-client"], "pb/#{Gem.loaded_specs["google-protobuf"].version}" + end + + def test_does_not_duplicate_pb_version + metadata = { :"x-goog-api-client" => "gl-ruby/1.2.3 pb/1.2.3" } + mi.request_response( + request: nil, + call: nil, + method: nil, + metadata: metadata + ) do + end + assert_equal "gl-ruby/1.2.3 pb/1.2.3", metadata[:"x-goog-api-client"] + end + + def test_skips_developer_token_if_cloud_org + mi_cloud = Google::Ads::GoogleAds::Interceptors::MetadataInterceptor.new( + "dev_token", + "login_id", + "linked_id", + true, + nil + ) + metadata = {} + mi_cloud.request_response( + request: nil, + call: nil, + method: nil, + metadata: metadata + ) do + end + assert_nil metadata[:"developer-token"] + end + + def test_appends_gaada_to_x_goog_api_client + mi_gaada = Google::Ads::GoogleAds::Interceptors::MetadataInterceptor.new( + "dev_token", + "login_id", + "linked_id", + false, + "1.2.3" + ) + metadata = { :"x-goog-api-client" => "gl-ruby/1.2.3" } + mi_gaada.request_response( + request: nil, + call: nil, + method: nil, + metadata: metadata + ) do + end + assert_includes metadata[:"x-goog-api-client"], "gaada/1.2.3" + assert_includes metadata[:"x-goog-api-client"], "pb/#{Gem.loaded_specs["google-protobuf"].version}" + end +end