From 1654a00bd67c14639368e51ac4f70b2edf633a5b Mon Sep 17 00:00:00 2001 From: dorasun Date: Thu, 16 Jul 2026 16:04:19 +0000 Subject: [PATCH 1/9] Changes needed for MPA support Change-Id: Iaefffce0a2955c3df4369affc6335fb9313151df --- .../invite_user_with_access_role.rb | 18 +- .../account_management/update_user_access.rb | 12 +- ...pprove_pending_multi_party_auth_reviews.rb | 190 ++++++++++++++++++ ...config.rb => google_ads_config.rb.template | 0 4 files changed, 213 insertions(+), 7 deletions(-) create mode 100644 examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb rename google_ads_config.rb => google_ads_config.rb.template (100%) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index d96de3e8f..4ce64fae7 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -38,11 +38,19 @@ def invite_user_with_access_role(customer_id, email_address, access_role) operation: operation, ) - # Prints out information of the created invitation. - puts "Customer user access invitation was sent for customerId = #{customer_id} " \ - "email address = '#{email_address}', " \ - "access role = '#{access_role}'." - # [END invite_user_with_access_role] + if !response.result.multi_party_auth_review.empty? + puts "A multi-party auth review was triggered. The MPA review resource " \ + "name is #{response.result.multi_party_auth_review}. Ask a second " \ + "administrator to approve this request to send the user access invitation. " \ + "See advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb " \ + "for an example on how to approve an MPA auth review using the API." + else + # Print out information of the created invitation. + puts "Customer user access invitation was sent for customerId = #{customer_id} " \ + "email address = '#{email_address}', " \ + "access role = '#{access_role}'. The invitation resource name is " \ + "#{response.result.resource_name}." + end end if __FILE__ == $PROGRAM_NAME diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index 8770082b2..468fe7bff 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -83,8 +83,16 @@ def modify_user_access(client, customer_id, user_id, access_role) operation: operation, ) - puts "Successfully updated customer user access with resource name " \ - "#{response.result.resource_name}." + if !response.result.multi_party_auth_review.empty? + puts "A multi-party auth review was triggered. The MPA review resource " \ + "name is #{response.result.multi_party_auth_review}. Ask a second " \ + "administrator to approve this request to make the requested user " \ + "access changes. See advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb " \ + "for an example on how to approve an MPA auth review using the API." + else + puts "Successfully updated customer user access with resource name " \ + "#{response.result.resource_name}." + end end if __FILE__ == $0 diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb new file mode 100644 index 000000000..6ac30d116 --- /dev/null +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -0,0 +1,190 @@ +#!/usr/bin/env ruby +# Encoding: utf-8 +# +# Copyright 2026 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. +# +# This example fetches pending multi-party approvals and approves the first one. +# +# Multi-party authorization (MPA) reviews can only be resolved one at a time. +# In this code example, we illustrate approving the first pending review request. + +require 'optparse' +require 'google/ads/google_ads' + +def fetch_and_approve_pending_multi_party_auth_reviews(customer_id) + # GoogleAdsClient will read a config file from + # ENV['HOME']/google_ads_config.rb when called without parameters + client = Google::Ads::GoogleAds::GoogleAdsClient.new + + # Retrieve the list of pending MPA reviews. + pending_reviews = fetch_pending_mpa_reviews(client, customer_id) + + if !pending_reviews.empty? + # Multi-party auth reviews can only be resolved one at a time. In this + # code example, we illustrate approving the first pending review request. + approve_mpa_review(client, customer_id, pending_reviews.first) + end +end + +# [START fetch_mpa_review] +def fetch_pending_mpa_reviews(client, customer_id) + pending_reviews = [] + + # Create a query that will retrieve all the pending MPA reviews. + query = <<~QUERY + SELECT + multi_party_auth_review.resource_name, + multi_party_auth_review.multi_party_auth_review_id, + multi_party_auth_review.creation_date_time, + multi_party_auth_review.request_user_email, + multi_party_auth_review.operation_type, + multi_party_auth_review.justification, + multi_party_auth_review.target_resource, + multi_party_auth_review.customer_user_access_review.old_customer_user_access, + multi_party_auth_review.customer_user_access_review.new_customer_user_access, + multi_party_auth_review.customer_user_access_invitation_review.new_customer_user_access_invitation + FROM multi_party_auth_review + WHERE multi_party_auth_review.review_status = 'PENDING' + QUERY + + responses = client.service.google_ads.search_stream( + customer_id: customer_id, + query: query, + ) + + responses.each do |response| + response.results.each do |row| + mpa_review = row.multi_party_auth_review + puts "#{mpa_review.request_user_email} created a pending " \ + "multi-party auth review with ID #{mpa_review.multi_party_auth_review_id} " \ + "at #{mpa_review.creation_date_time}. This request is for target " \ + "resource type = #{mpa_review.target_resource} and operation type = " \ + "#{mpa_review.operation_type}. The justification is " \ + "\"#{mpa_review.justification}\"." + + if mpa_review.target_resource == :CUSTOMER_USER_ACCESS + access_review = mpa_review.customer_user_access_review + if mpa_review.operation_type == :UPDATE + # When updating a customer user access, only the new access level + # is populated. + puts "Old resource name: #{access_review.old_customer_user_access}, " \ + "new access role: #{access_review.new_customer_user_access.access_role}." + elsif mpa_review.operation_type == :REMOVE + puts "Old resource name: #{access_review.old_customer_user_access}." + end + elsif mpa_review.target_resource == :CUSTOMER_USER_ACCESS_INVITATION + new_invite = mpa_review.customer_user_access_invitation_review.new_customer_user_access_invitation + puts "Invitation email address: #{new_invite.email_address}, " \ + "Role: #{new_invite.access_role}." + end + + pending_reviews << mpa_review.resource_name + end + end + + pending_reviews +end +# [END fetch_mpa_review] + +# [START approve_mpa_review] +def approve_mpa_review(client, customer_id, pending_review) + # Currently, you can only approve one request at a time. In addition, the approvals + # can only be done by a second administrator. + response = client.service.multi_party_auth_review.resolve_multi_party_auth_review( + customer_id: customer_id, + operations: [ + { + multi_party_auth_review: pending_review, + new_status: :APPROVED, + } + ], + ) + + result_or_error = response.result_or_error.first + + if result_or_error.result + result = result_or_error.result + puts "Approved multi-party auth review: #{result.multi_party_auth_review}." + if !result.customer_user_access_invitation.empty? + puts "New user invitation created: #{result.customer_user_access_invitation}" + elsif !result.customer_user_access.empty? + puts "Affected customer user access resource: #{result.customer_user_access}" + end + elsif result_or_error.partial_failure_error + # Partial failure error + errors_count = 0 + failures = client.decode_partial_failure_error(result_or_error.partial_failure_error) + failures.each do |failure| + failure.errors.each do |error| + puts "\tError with message '#{error.message}'." + errors_count += 1 + end + end + puts "#{errors_count} partial failure error(s) occurred" + end +end +# [END approve_mpa_review] + +if __FILE__ == $PROGRAM_NAME + options = {} + # The following parameter(s) should be provided to run the example. You can + # either specify these by changing the INSERT_XXX_ID_HERE values below, or on + # the command line. + # + # Parameters passed on the command line will override any parameters set in + # code. + # + # Running the example with -h will print the command line usage. + options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE' + + OptionParser.new do |opts| + opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__)) + + opts.separator '' + opts.separator 'Options:' + + opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v| + options[:customer_id] = v + end + + opts.separator '' + opts.separator 'Help:' + + opts.on_tail('-h', '--help', 'Show this message') do + puts opts + exit + end + end.parse! + + begin + fetch_and_approve_pending_multi_party_auth_reviews( + options.fetch(:customer_id).tr("-", "") + ) + rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e + e.failure.errors.each do |error| + STDERR.printf("Error with message: %s\n", error.message) + if error.location + error.location.field_path_elements.each do |field_path_element| + STDERR.printf("\tOn field: %s\n", field_path_element.field_name) + end + end + error.error_code.to_h.each do |k, v| + next if v == :UNSPECIFIED + STDERR.printf("\tType: %s\n\tCode: %s\n", k, v) + end + end + raise + end +end diff --git a/google_ads_config.rb b/google_ads_config.rb.template similarity index 100% rename from google_ads_config.rb rename to google_ads_config.rb.template From 7d2c9d36e85c2f09cb881bbaecce017ea3999afd Mon Sep 17 00:00:00 2001 From: dorasun Date: Thu, 16 Jul 2026 21:17:11 +0000 Subject: [PATCH 2/9] Respond to comments Change-Id: Ie7f15d5ef3968dd177b8eddfbf43b566f72fca3c --- .../invite_user_with_access_role.rb | 21 ++++++++++++++++++- .../account_management/update_user_access.rb | 18 +++++++++++++--- ...pprove_pending_multi_party_auth_reviews.rb | 20 +++++++++++++----- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index 4ce64fae7..fa742e176 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -54,6 +54,13 @@ def invite_user_with_access_role(customer_id, email_address, access_role) end if __FILE__ == $PROGRAM_NAME + ACCESS_ROLES = %w[ + ADMIN + STANDARD + READ_ONLY + EMAIL_ONLY + ] + options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on @@ -94,11 +101,23 @@ def invite_user_with_access_role(customer_id, email_address, access_role) end end.parse! + if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' || + options[:email_address].nil? || options[:email_address] == 'INSERT_EMAIL_ADDRESS_HERE' || + options[:access_role].nil? || options[:access_role] == 'INSERT_ACCESS_ROLE_HERE' + puts "Missing required arguments. Customer ID (-C), Email Address (-E), and Access Role (-R) are required." + exit 1 + end + + unless ACCESS_ROLES.include?(options[:access_role]&.upcase) + puts "Invalid access role. Must be one of: #{ACCESS_ROLES.join(', ')}" + exit 1 + end + begin invite_user_with_access_role( options.fetch(:customer_id).tr("-", ""), options.fetch(:email_address), - options.fetch(:access_role).to_sym, + options.fetch(:access_role).upcase.to_sym, ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e e.failure.errors.each do |error| diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index 468fe7bff..9587704c6 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -93,9 +93,9 @@ def modify_user_access(client, customer_id, user_id, access_role) puts "Successfully updated customer user access with resource name " \ "#{response.result.resource_name}." end -end +end -if __FILE__ == $0 +if __FILE__ == $PROGRAM_NAME ACCESS_ROLES = %w[ ADMIN STANDARD @@ -143,11 +143,23 @@ def modify_user_access(client, customer_id, user_id, access_role) end end.parse! + if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' || + options[:email_address].nil? || options[:email_address] == 'INSERT_EMAIL_ADDRESS_HERE' || + options[:access_role].nil? || options[:access_role] == 'INSERT_ACCESS_ROLE_HERE' + puts "Missing required arguments. Customer ID (-C), Email Address (-e), and Access Role (-a) are required." + exit 1 + end + + unless ACCESS_ROLES.include?(options[:access_role]&.upcase) + puts "Invalid access role. Must be one of: #{ACCESS_ROLES.join(', ')}" + exit 1 + end + begin update_user_access( options.fetch(:customer_id).tr("-", ""), options.fetch(:email_address), - options.fetch(:access_role), + options.fetch(:access_role).upcase, ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e e.failure.errors.each do |error| diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb index 6ac30d116..8b126cc8f 100644 --- a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -35,6 +35,8 @@ def fetch_and_approve_pending_multi_party_auth_reviews(customer_id) # Multi-party auth reviews can only be resolved one at a time. In this # code example, we illustrate approving the first pending review request. approve_mpa_review(client, customer_id, pending_reviews.first) + else + puts "No pending multi-party auth reviews found for customer ID #{customer_id}." end end @@ -48,6 +50,7 @@ def fetch_pending_mpa_reviews(client, customer_id) multi_party_auth_review.resource_name, multi_party_auth_review.multi_party_auth_review_id, multi_party_auth_review.creation_date_time, + multi_party_auth_review.review_status, multi_party_auth_review.request_user_email, multi_party_auth_review.operation_type, multi_party_auth_review.justification, @@ -79,14 +82,14 @@ def fetch_pending_mpa_reviews(client, customer_id) if mpa_review.operation_type == :UPDATE # When updating a customer user access, only the new access level # is populated. - puts "Old resource name: #{access_review.old_customer_user_access}, " \ + puts "\tOld resource name: #{access_review.old_customer_user_access}, " \ "new access role: #{access_review.new_customer_user_access.access_role}." elsif mpa_review.operation_type == :REMOVE - puts "Old resource name: #{access_review.old_customer_user_access}." + puts "\tOld resource name: #{access_review.old_customer_user_access}." end elsif mpa_review.target_resource == :CUSTOMER_USER_ACCESS_INVITATION new_invite = mpa_review.customer_user_access_invitation_review.new_customer_user_access_invitation - puts "Invitation email address: #{new_invite.email_address}, " \ + puts "\tInvitation email address: #{new_invite.email_address}, " \ "Role: #{new_invite.access_role}." end @@ -114,7 +117,7 @@ def approve_mpa_review(client, customer_id, pending_review) result_or_error = response.result_or_error.first - if result_or_error.result + if result_or_error&.result result = result_or_error.result puts "Approved multi-party auth review: #{result.multi_party_auth_review}." if !result.customer_user_access_invitation.empty? @@ -122,7 +125,7 @@ def approve_mpa_review(client, customer_id, pending_review) elsif !result.customer_user_access.empty? puts "Affected customer user access resource: #{result.customer_user_access}" end - elsif result_or_error.partial_failure_error + elsif result_or_error&.partial_failure_error # Partial failure error errors_count = 0 failures = client.decode_partial_failure_error(result_or_error.partial_failure_error) @@ -133,6 +136,8 @@ def approve_mpa_review(client, customer_id, pending_review) end end puts "#{errors_count} partial failure error(s) occurred" + else + puts "No result or error returned." end end # [END approve_mpa_review] @@ -168,6 +173,11 @@ def approve_mpa_review(client, customer_id, pending_review) end end.parse! + if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' + puts "Missing required argument: --customer-id (-C) is required." + exit 1 + end + begin fetch_and_approve_pending_multi_party_auth_reviews( options.fetch(:customer_id).tr("-", "") From 19228ffe1f9ea2aa9ef836c00352ac277efaa086 Mon Sep 17 00:00:00 2001 From: dorasun Date: Fri, 7 Aug 2026 20:38:40 +0000 Subject: [PATCH 3/9] Fixing whitespace, standardizing flags, adding back missing end tag, improving validation Change-Id: I61c181257475a3311c9b775de7488963ad6a4a70 --- .../invite_user_with_access_role.rb | 3 ++- .../account_management/update_user_access.rb | 20 +++++++++---------- ...pprove_pending_multi_party_auth_reviews.rb | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index fa742e176..606f3b65f 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -43,7 +43,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) "name is #{response.result.multi_party_auth_review}. Ask a second " \ "administrator to approve this request to send the user access invitation. " \ "See advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb " \ - "for an example on how to approve an MPA auth review using the API." + "for an example on how to approve an MPA auth review using the API." else # Print out information of the created invitation. puts "Customer user access invitation was sent for customerId = #{customer_id} " \ @@ -51,6 +51,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) "access role = '#{access_role}'. The invitation resource name is " \ "#{response.result.resource_name}." end + # [END invite_user_with_access_role] end if __FILE__ == $PROGRAM_NAME diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index 9587704c6..de95282ac 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -26,6 +26,13 @@ require 'optparse' require 'google/ads/google_ads' +ACCESS_ROLES = %w[ + ADMIN + STANDARD + READ_ONLY + EMAIL_ONLY +] + def update_user_access(customer_id, email_address, access_role) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters @@ -96,13 +103,6 @@ def modify_user_access(client, customer_id, user_id, access_role) end if __FILE__ == $PROGRAM_NAME - ACCESS_ROLES = %w[ - ADMIN - STANDARD - READ_ONLY - EMAIL_ONLY - ] - options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on @@ -126,11 +126,11 @@ def modify_user_access(client, customer_id, user_id, access_role) options[:customer_id] = v end - opts.on('-e', '--email-address EMAIL-ADDRESS', String, 'Email Address') do |v| + opts.on('-E', '--email-address EMAIL-ADDRESS', String, 'Email Address') do |v| options[:email_address] = v end - opts.on('-a', '--access-role ACCESS-ROLE', String, 'Access Role') do |v| + opts.on('-R', '--access-role ACCESS-ROLE', String, 'Access Role') do |v| options[:access_role] = v end @@ -146,7 +146,7 @@ def modify_user_access(client, customer_id, user_id, access_role) if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' || options[:email_address].nil? || options[:email_address] == 'INSERT_EMAIL_ADDRESS_HERE' || options[:access_role].nil? || options[:access_role] == 'INSERT_ACCESS_ROLE_HERE' - puts "Missing required arguments. Customer ID (-C), Email Address (-e), and Access Role (-a) are required." + puts "Missing required arguments. Customer ID (-C), Email Address (-E), and Access Role (-R) are required." exit 1 end diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb index 8b126cc8f..186376301 100644 --- a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -120,9 +120,9 @@ def approve_mpa_review(client, customer_id, pending_review) if result_or_error&.result result = result_or_error.result puts "Approved multi-party auth review: #{result.multi_party_auth_review}." - if !result.customer_user_access_invitation.empty? + if !result.customer_user_access_invitation.to_s.empty? puts "New user invitation created: #{result.customer_user_access_invitation}" - elsif !result.customer_user_access.empty? + elsif !result.customer_user_access.to_s.empty? puts "Affected customer user access resource: #{result.customer_user_access}" end elsif result_or_error&.partial_failure_error From 368802744d9ce41d455267dc46e62f96e21d532a Mon Sep 17 00:00:00 2001 From: dorasun Date: Fri, 7 Aug 2026 20:47:23 +0000 Subject: [PATCH 4/9] Freezing access role arrays Change-Id: Idebe3ddca986d7e88884d28f3e000ffc566c4d1b --- .../invite_user_with_access_role.rb | 14 +++++++------- examples/account_management/update_user_access.rb | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index 606f3b65f..f6fc5494d 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -21,6 +21,13 @@ require 'optparse' require 'google/ads/google_ads' +ACCESS_ROLES = %w[ + ADMIN + STANDARD + READ_ONLY + EMAIL_ONLY +].freeze + def invite_user_with_access_role(customer_id, email_address, access_role) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters @@ -55,13 +62,6 @@ def invite_user_with_access_role(customer_id, email_address, access_role) end if __FILE__ == $PROGRAM_NAME - ACCESS_ROLES = %w[ - ADMIN - STANDARD - READ_ONLY - EMAIL_ONLY - ] - options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index de95282ac..f7c5bcc1c 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -31,7 +31,7 @@ STANDARD READ_ONLY EMAIL_ONLY -] +].freeze def update_user_access(customer_id, email_address, access_role) # GoogleAdsClient will read a config file from From 70a23076a3574314a19455d716374ce7a7298375 Mon Sep 17 00:00:00 2001 From: dorasun Date: Wed, 12 Aug 2026 18:45:55 +0000 Subject: [PATCH 5/9] Address PR comments on invite_user_with_access_role.rb and update_user_access.rb Change-Id: Ic23d5af07d153674d7336edb0d4fdb6193c7aeaa --- .../invite_user_with_access_role.rb | 17 +++++++++----- .../account_management/update_user_access.rb | 23 +++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index f6fc5494d..3b8fb4e71 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # Encoding: utf-8 # -# Copyright 2021 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,6 +33,9 @@ def invite_user_with_access_role(customer_id, email_address, access_role) # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new + # Sanitizes the customer ID to handle hyphenated inputs. + customer_id = customer_id.to_s.tr('-', '') + # [START invite_user_with_access_role] operation = client.operation.create_resource.customer_user_access_invitation do |inv| inv.email_address = email_address @@ -45,7 +48,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) operation: operation, ) - if !response.result.multi_party_auth_review.empty? + if !response.result.multi_party_auth_review.to_s.empty? puts "A multi-party auth review was triggered. The MPA review resource " \ "name is #{response.result.multi_party_auth_review}. Ask a second " \ "administrator to approve this request to send the user access invitation. " \ @@ -75,7 +78,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) options[:email_address] = 'INSERT_EMAIL_ADDRESS_HERE' options[:access_role] = 'INSERT_ACCESS_ROLE_HERE' - OptionParser.new do |opts| + parser = OptionParser.new do |opts| opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__)) opts.separator '' @@ -100,12 +103,14 @@ def invite_user_with_access_role(customer_id, email_address, access_role) puts opts exit end - end.parse! + end + parser.parse! if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' || options[:email_address].nil? || options[:email_address] == 'INSERT_EMAIL_ADDRESS_HERE' || options[:access_role].nil? || options[:access_role] == 'INSERT_ACCESS_ROLE_HERE' - puts "Missing required arguments. Customer ID (-C), Email Address (-E), and Access Role (-R) are required." + puts "Missing required arguments. Customer ID (-C), Email Address (-E), and Access Role (-R) are required.\n\n" + puts parser exit 1 end @@ -116,7 +121,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) begin invite_user_with_access_role( - options.fetch(:customer_id).tr("-", ""), + options.fetch(:customer_id), options.fetch(:email_address), options.fetch(:access_role).upcase.to_sym, ) diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index f7c5bcc1c..0ff72e963 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # Encoding: utf-8 # -# Copyright 2020 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,6 +38,9 @@ def update_user_access(customer_id, email_address, access_role) # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new + # Sanitizes the customer ID to handle hyphenated inputs. + customer_id = customer_id.to_s.tr('-', '') + if !ACCESS_ROLES.include?(access_role) raise "Illegal access role specified. Expected one of " \ "#{ACCESS_ROLES.join(" ")}" @@ -90,7 +93,7 @@ def modify_user_access(client, customer_id, user_id, access_role) operation: operation, ) - if !response.result.multi_party_auth_review.empty? + if !response.result.multi_party_auth_review.to_s.empty? puts "A multi-party auth review was triggered. The MPA review resource " \ "name is #{response.result.multi_party_auth_review}. Ask a second " \ "administrator to approve this request to make the requested user " \ @@ -100,9 +103,9 @@ def modify_user_access(client, customer_id, user_id, access_role) puts "Successfully updated customer user access with resource name " \ "#{response.result.resource_name}." end -end +end -if __FILE__ == $PROGRAM_NAME +if __FILE__ == $0 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on @@ -116,7 +119,7 @@ def modify_user_access(client, customer_id, user_id, access_role) options[:email_address] = 'INSERT_EMAIL_ADDRESS_HERE' options[:access_role] = 'INSERT_ACCESS_ROLE_HERE' - OptionParser.new do |opts| + parser = OptionParser.new do |opts| opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' @@ -141,12 +144,14 @@ def modify_user_access(client, customer_id, user_id, access_role) puts opts exit end - end.parse! + end + parser.parse! if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' || options[:email_address].nil? || options[:email_address] == 'INSERT_EMAIL_ADDRESS_HERE' || options[:access_role].nil? || options[:access_role] == 'INSERT_ACCESS_ROLE_HERE' - puts "Missing required arguments. Customer ID (-C), Email Address (-E), and Access Role (-R) are required." + puts "Missing required arguments. Customer ID (-C), Email Address (-E), and Access Role (-R) are required.\n\n" + puts parser exit 1 end @@ -157,9 +162,9 @@ def modify_user_access(client, customer_id, user_id, access_role) begin update_user_access( - options.fetch(:customer_id).tr("-", ""), + options.fetch(:customer_id), options.fetch(:email_address), - options.fetch(:access_role).upcase, + options.fetch(:access_role).upcase.to_sym, ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e e.failure.errors.each do |error| From cd28040fd88efae5cff839861db2b8083e1c5496 Mon Sep 17 00:00:00 2001 From: dorasun Date: Thu, 13 Aug 2026 18:40:55 +0000 Subject: [PATCH 6/9] Improve consistency, null handling Change-Id: I0f5ecf8159a2534b6ae36cbb331020636ab2bf6d --- ...h_and_approve_pending_multi_party_auth_reviews.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb index 186376301..12989d8a6 100644 --- a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -83,7 +83,7 @@ def fetch_pending_mpa_reviews(client, customer_id) # When updating a customer user access, only the new access level # is populated. puts "\tOld resource name: #{access_review.old_customer_user_access}, " \ - "new access role: #{access_review.new_customer_user_access.access_role}." + "new access role: #{access_review.new_customer_user_access&.access_role}." elsif mpa_review.operation_type == :REMOVE puts "\tOld resource name: #{access_review.old_customer_user_access}." end @@ -154,8 +154,8 @@ def approve_mpa_review(client, customer_id, pending_review) # Running the example with -h will print the command line usage. options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE' - OptionParser.new do |opts| - opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__)) + parser = OptionParser.new do |opts| + opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' opts.separator 'Options:' @@ -171,10 +171,12 @@ def approve_mpa_review(client, customer_id, pending_review) puts opts exit end - end.parse! + end + parser.parse! if options[:customer_id].nil? || options[:customer_id] == 'INSERT_CUSTOMER_ID_HERE' - puts "Missing required argument: --customer-id (-C) is required." + puts "Missing required argument: --customer-id (-C) is required.\n\n" + puts parser exit 1 end From 0d05a284c1f27ebd03f7b97c6a631d277ae89355 Mon Sep 17 00:00:00 2001 From: Bob Hancock Date: Tue, 15 Sep 2026 14:28:02 -0400 Subject: [PATCH 7/9] Optimize MPA examples: fix role validation, safe navigation, error handling, and restore config --- .../invite_user_with_access_role.rb | 21 +++++++++---- .../account_management/update_user_access.rb | 24 ++++++++------ ...pprove_pending_multi_party_auth_reviews.rb | 31 ++++++++++++------- ...config.rb.template => google_ads_config.rb | 0 4 files changed, 48 insertions(+), 28 deletions(-) rename google_ads_config.rb.template => google_ads_config.rb (100%) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index 3b8fb4e71..7ca928ee7 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -36,6 +36,13 @@ def invite_user_with_access_role(customer_id, email_address, access_role) # Sanitizes the customer ID to handle hyphenated inputs. customer_id = customer_id.to_s.tr('-', '') + access_role_str = access_role.to_s.upcase + if !ACCESS_ROLES.include?(access_role_str) + raise "Illegal access role specified. Expected one of " \ + "#{ACCESS_ROLES.join(' ')}" + end + access_role = access_role_str.to_sym + # [START invite_user_with_access_role] operation = client.operation.create_resource.customer_user_access_invitation do |inv| inv.email_address = email_address @@ -48,9 +55,9 @@ def invite_user_with_access_role(customer_id, email_address, access_role) operation: operation, ) - if !response.result.multi_party_auth_review.to_s.empty? + if !response.result&.multi_party_auth_review.to_s.empty? puts "A multi-party auth review was triggered. The MPA review resource " \ - "name is #{response.result.multi_party_auth_review}. Ask a second " \ + "name is #{response.result&.multi_party_auth_review}. Ask a second " \ "administrator to approve this request to send the user access invitation. " \ "See advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb " \ "for an example on how to approve an MPA auth review using the API." @@ -59,7 +66,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) puts "Customer user access invitation was sent for customerId = #{customer_id} " \ "email address = '#{email_address}', " \ "access role = '#{access_role}'. The invitation resource name is " \ - "#{response.result.resource_name}." + "#{response.result&.resource_name}." end # [END invite_user_with_access_role] end @@ -126,16 +133,18 @@ def invite_user_with_access_role(customer_id, email_address, access_role) options.fetch(:access_role).upcase.to_sym, ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e + STDERR.printf("Request with ID '%s' failed with status '%s' and includes the following errors:\n", + e.request_id, e.class.name) e.failure.errors.each do |error| - STDERR.printf("Error with message: %s\n", error.message) + STDERR.printf("\tError with message: %s\n", error.message) if error.location error.location.field_path_elements.each do |field_path_element| - STDERR.printf("\tOn field: %s\n", field_path_element.field_name) + STDERR.printf("\t\tOn field: %s\n", field_path_element.field_name) end end error.error_code.to_h.each do |k, v| next if v == :UNSPECIFIED - STDERR.printf("\tType: %s\n\tCode: %s\n", k, v) + STDERR.printf("\t\tType: %s, Code: %s\n", k, v) end end raise diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index 0ff72e963..f0f96a093 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -41,10 +41,12 @@ def update_user_access(customer_id, email_address, access_role) # Sanitizes the customer ID to handle hyphenated inputs. customer_id = customer_id.to_s.tr('-', '') - if !ACCESS_ROLES.include?(access_role) + access_role_str = access_role.to_s.upcase + if !ACCESS_ROLES.include?(access_role_str) raise "Illegal access role specified. Expected one of " \ - "#{ACCESS_ROLES.join(" ")}" + "#{ACCESS_ROLES.join(' ')}" end + access_role = access_role_str.to_sym user_id = get_user_id(client, customer_id, email_address) @@ -69,7 +71,7 @@ def get_user_id(client, customer_id, email_address) query: query, ) - result = response.first.customer_user_access + result = response.first&.customer_user_access if result puts "Customer user access with User ID #{result.user_id}, Access Role " \ "#{result.access_role}, Creation Time #{result.access_creation_date_time}, " \ @@ -93,19 +95,19 @@ def modify_user_access(client, customer_id, user_id, access_role) operation: operation, ) - if !response.result.multi_party_auth_review.to_s.empty? + if !response.result&.multi_party_auth_review.to_s.empty? puts "A multi-party auth review was triggered. The MPA review resource " \ - "name is #{response.result.multi_party_auth_review}. Ask a second " \ + "name is #{response.result&.multi_party_auth_review}. Ask a second " \ "administrator to approve this request to make the requested user " \ "access changes. See advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb " \ "for an example on how to approve an MPA auth review using the API." else puts "Successfully updated customer user access with resource name " \ - "#{response.result.resource_name}." + "#{response.result&.resource_name}." end end -if __FILE__ == $0 +if __FILE__ == $PROGRAM_NAME options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on @@ -167,16 +169,18 @@ def modify_user_access(client, customer_id, user_id, access_role) options.fetch(:access_role).upcase.to_sym, ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e + STDERR.printf("Request with ID '%s' failed with status '%s' and includes the following errors:\n", + e.request_id, e.class.name) e.failure.errors.each do |error| - STDERR.printf("Error with message: %s\n", error.message) + STDERR.printf("\tError with message: %s\n", error.message) if error.location error.location.field_path_elements.each do |field_path_element| - STDERR.printf("\tOn field: %s\n", field_path_element.field_name) + STDERR.printf("\t\tOn field: %s\n", field_path_element.field_name) end end error.error_code.to_h.each do |k, v| next if v == :UNSPECIFIED - STDERR.printf("\tType: %s\n\tCode: %s\n", k, v) + STDERR.printf("\t\tType: %s, Code: %s\n", k, v) end end raise diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb index 12989d8a6..61dde0282 100644 --- a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -28,6 +28,9 @@ def fetch_and_approve_pending_multi_party_auth_reviews(customer_id) # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new + # Sanitizes the customer ID to handle hyphenated inputs. + customer_id = customer_id.to_s.tr('-', '') + # Retrieve the list of pending MPA reviews. pending_reviews = fetch_pending_mpa_reviews(client, customer_id) @@ -42,6 +45,7 @@ def fetch_and_approve_pending_multi_party_auth_reviews(customer_id) # [START fetch_mpa_review] def fetch_pending_mpa_reviews(client, customer_id) + customer_id = customer_id.to_s.tr('-', '') pending_reviews = [] # Create a query that will retrieve all the pending MPA reviews. @@ -82,15 +86,15 @@ def fetch_pending_mpa_reviews(client, customer_id) if mpa_review.operation_type == :UPDATE # When updating a customer user access, only the new access level # is populated. - puts "\tOld resource name: #{access_review.old_customer_user_access}, " \ - "new access role: #{access_review.new_customer_user_access&.access_role}." + puts "\tOld resource name: #{access_review&.old_customer_user_access}, " \ + "new access role: #{access_review&.new_customer_user_access&.access_role}." elsif mpa_review.operation_type == :REMOVE - puts "\tOld resource name: #{access_review.old_customer_user_access}." + puts "\tOld resource name: #{access_review&.old_customer_user_access}." end elsif mpa_review.target_resource == :CUSTOMER_USER_ACCESS_INVITATION - new_invite = mpa_review.customer_user_access_invitation_review.new_customer_user_access_invitation - puts "\tInvitation email address: #{new_invite.email_address}, " \ - "Role: #{new_invite.access_role}." + new_invite = mpa_review.customer_user_access_invitation_review&.new_customer_user_access_invitation + puts "\tInvitation email address: #{new_invite&.email_address}, " \ + "Role: #{new_invite&.access_role}." end pending_reviews << mpa_review.resource_name @@ -103,6 +107,7 @@ def fetch_pending_mpa_reviews(client, customer_id) # [START approve_mpa_review] def approve_mpa_review(client, customer_id, pending_review) + customer_id = customer_id.to_s.tr('-', '') # Currently, you can only approve one request at a time. In addition, the approvals # can only be done by a second administrator. response = client.service.multi_party_auth_review.resolve_multi_party_auth_review( @@ -115,7 +120,7 @@ def approve_mpa_review(client, customer_id, pending_review) ], ) - result_or_error = response.result_or_error.first + result_or_error = response.result_or_error&.first if result_or_error&.result result = result_or_error.result @@ -128,7 +133,7 @@ def approve_mpa_review(client, customer_id, pending_review) elsif result_or_error&.partial_failure_error # Partial failure error errors_count = 0 - failures = client.decode_partial_failure_error(result_or_error.partial_failure_error) + failures = client.decode_partial_failure_error(result_or_error.partial_failure_error) || [] failures.each do |failure| failure.errors.each do |error| puts "\tError with message '#{error.message}'." @@ -182,19 +187,21 @@ def approve_mpa_review(client, customer_id, pending_review) begin fetch_and_approve_pending_multi_party_auth_reviews( - options.fetch(:customer_id).tr("-", "") + options.fetch(:customer_id) ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e + STDERR.printf("Request with ID '%s' failed with status '%s' and includes the following errors:\n", + e.request_id, e.class.name) e.failure.errors.each do |error| - STDERR.printf("Error with message: %s\n", error.message) + STDERR.printf("\tError with message: %s\n", error.message) if error.location error.location.field_path_elements.each do |field_path_element| - STDERR.printf("\tOn field: %s\n", field_path_element.field_name) + STDERR.printf("\t\tOn field: %s\n", field_path_element.field_name) end end error.error_code.to_h.each do |k, v| next if v == :UNSPECIFIED - STDERR.printf("\tType: %s\n\tCode: %s\n", k, v) + STDERR.printf("\t\tType: %s, Code: %s\n", k, v) end end raise diff --git a/google_ads_config.rb.template b/google_ads_config.rb similarity index 100% rename from google_ads_config.rb.template rename to google_ads_config.rb From 6aa4b0cfd37cced4f13322d343e7294d4c1ae7e3 Mon Sep 17 00:00:00 2001 From: dorasun Date: Tue, 13 Oct 2026 18:02:32 +0000 Subject: [PATCH 8/9] Standardize script entrypoint check to $0 and clean usage banner Change-Id: I110442d56bc5f139d774b15f5af0078980afa87b --- examples/account_management/invite_user_with_access_role.rb | 4 ++-- examples/account_management/update_user_access.rb | 2 +- .../fetch_and_approve_pending_multi_party_auth_reviews.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index 7ca928ee7..63f5b6d69 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -71,7 +71,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) # [END invite_user_with_access_role] end -if __FILE__ == $PROGRAM_NAME +if __FILE__ == $0 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on @@ -86,7 +86,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) options[:access_role] = 'INSERT_ACCESS_ROLE_HERE' parser = OptionParser.new do |opts| - opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__)) + opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' opts.separator 'Options:' diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index f0f96a093..f7a53b574 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -107,7 +107,7 @@ def modify_user_access(client, customer_id, user_id, access_role) end end -if __FILE__ == $PROGRAM_NAME +if __FILE__ == $0 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb index 61dde0282..3d5074e8d 100644 --- a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -147,7 +147,7 @@ def approve_mpa_review(client, customer_id, pending_review) end # [END approve_mpa_review] -if __FILE__ == $PROGRAM_NAME +if __FILE__ == $0 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on From e2f442fe3b3ff33f21d70d7eac3fb452c74b5b71 Mon Sep 17 00:00:00 2001 From: dorasun Date: Wed, 14 Oct 2026 16:24:21 +0000 Subject: [PATCH 9/9] Address review comments on MPA examples: operation factory, error handling, query ordering, permissions, and argument typing Change-Id: I38eac75318bb939bb11def66ed1bc1bff51da18e --- .../invite_user_with_access_role.rb | 6 ++--- .../account_management/update_user_access.rb | 9 ++++--- ...pprove_pending_multi_party_auth_reviews.rb | 27 ++++++++++++------- 3 files changed, 25 insertions(+), 17 deletions(-) mode change 100644 => 100755 examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb diff --git a/examples/account_management/invite_user_with_access_role.rb b/examples/account_management/invite_user_with_access_role.rb index 63f5b6d69..3e66b2ccc 100755 --- a/examples/account_management/invite_user_with_access_role.rb +++ b/examples/account_management/invite_user_with_access_role.rb @@ -38,7 +38,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) access_role_str = access_role.to_s.upcase if !ACCESS_ROLES.include?(access_role_str) - raise "Illegal access role specified. Expected one of " \ + raise ArgumentError, "Illegal access role specified. Expected one of " \ "#{ACCESS_ROLES.join(' ')}" end access_role = access_role_str.to_sym @@ -66,7 +66,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) puts "Customer user access invitation was sent for customerId = #{customer_id} " \ "email address = '#{email_address}', " \ "access role = '#{access_role}'. The invitation resource name is " \ - "#{response.result&.resource_name}." + "#{response.result&.resource_name || 'N/A'}." end # [END invite_user_with_access_role] end @@ -130,7 +130,7 @@ def invite_user_with_access_role(customer_id, email_address, access_role) invite_user_with_access_role( options.fetch(:customer_id), options.fetch(:email_address), - options.fetch(:access_role).upcase.to_sym, + options.fetch(:access_role), ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e STDERR.printf("Request with ID '%s' failed with status '%s' and includes the following errors:\n", diff --git a/examples/account_management/update_user_access.rb b/examples/account_management/update_user_access.rb index f7a53b574..44cd022aa 100755 --- a/examples/account_management/update_user_access.rb +++ b/examples/account_management/update_user_access.rb @@ -43,7 +43,7 @@ def update_user_access(customer_id, email_address, access_role) access_role_str = access_role.to_s.upcase if !ACCESS_ROLES.include?(access_role_str) - raise "Illegal access role specified. Expected one of " \ + raise ArgumentError, "Illegal access role specified. Expected one of " \ "#{ACCESS_ROLES.join(' ')}" end access_role = access_role_str.to_sym @@ -64,6 +64,7 @@ def get_user_id(client, customer_id, email_address) customer_user_access.access_creation_date_time FROM customer_user_access WHERE customer_user_access.email_address LIKE "#{email_address}" + LIMIT 1 QUERY response = client.service.google_ads.search( @@ -87,7 +88,7 @@ def get_user_id(client, customer_id, email_address) def modify_user_access(client, customer_id, user_id, access_role) operation = client.operation.update_resource.customer_user_access( client.path.customer_user_access(customer_id, user_id)) do |ua| - ua.access_role = access_role.to_sym + ua.access_role = access_role end response = client.service.customer_user_access.mutate_customer_user_access( @@ -103,7 +104,7 @@ def modify_user_access(client, customer_id, user_id, access_role) "for an example on how to approve an MPA auth review using the API." else puts "Successfully updated customer user access with resource name " \ - "#{response.result&.resource_name}." + "#{response.result&.resource_name || 'N/A'}." end end @@ -166,7 +167,7 @@ def modify_user_access(client, customer_id, user_id, access_role) update_user_access( options.fetch(:customer_id), options.fetch(:email_address), - options.fetch(:access_role).upcase.to_sym, + options.fetch(:access_role), ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e STDERR.printf("Request with ID '%s' failed with status '%s' and includes the following errors:\n", diff --git a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb old mode 100644 new mode 100755 index 3d5074e8d..1065bf5af --- a/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb +++ b/examples/advanced_operations/fetch_and_approve_pending_multi_party_auth_reviews.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# Encoding: utf-8 +# frozen_string_literal: true # # Copyright 2026 Google LLC # @@ -64,6 +64,7 @@ def fetch_pending_mpa_reviews(client, customer_id) multi_party_auth_review.customer_user_access_invitation_review.new_customer_user_access_invitation FROM multi_party_auth_review WHERE multi_party_auth_review.review_status = 'PENDING' + ORDER BY multi_party_auth_review.creation_date_time ASC QUERY responses = client.service.google_ads.search_stream( @@ -93,8 +94,10 @@ def fetch_pending_mpa_reviews(client, customer_id) end elsif mpa_review.target_resource == :CUSTOMER_USER_ACCESS_INVITATION new_invite = mpa_review.customer_user_access_invitation_review&.new_customer_user_access_invitation - puts "\tInvitation email address: #{new_invite&.email_address}, " \ - "Role: #{new_invite&.access_role}." + if new_invite + puts "\tInvitation email address: #{new_invite.email_address}, " \ + "Role: #{new_invite.access_role}." + end end pending_reviews << mpa_review.resource_name @@ -110,14 +113,14 @@ def approve_mpa_review(client, customer_id, pending_review) customer_id = customer_id.to_s.tr('-', '') # Currently, you can only approve one request at a time. In addition, the approvals # can only be done by a second administrator. + operation = client.operation.resolve_multi_party_auth_review do |op| + op.multi_party_auth_review = pending_review + op.new_status = :APPROVED + end + response = client.service.multi_party_auth_review.resolve_multi_party_auth_review( customer_id: customer_id, - operations: [ - { - multi_party_auth_review: pending_review, - new_status: :APPROVED, - } - ], + operations: [operation], ) result_or_error = response.result_or_error&.first @@ -140,7 +143,11 @@ def approve_mpa_review(client, customer_id, pending_review) errors_count += 1 end end - puts "#{errors_count} partial failure error(s) occurred" + if errors_count.zero? + puts "Partial failure error occurred: #{result_or_error.partial_failure_error.message}" + else + puts "#{errors_count} partial failure error(s) occurred" + end else puts "No result or error returned." end