Repository navigation
Changes needed for MPA support #555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1654a00
7d2c9d3
7cce923
19228ff
3688027
70a2307
cd28040
0d05a28
6aa4b0c
e2f442f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
|
@@ -21,11 +21,28 @@ | |
| 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 | ||
| client = Google::Ads::GoogleAds::GoogleAdsClient.new | ||
|
|
||
| # 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 ArgumentError, "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 | ||
|
|
@@ -38,14 +55,23 @@ 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}'." | ||
| 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. " \ | ||
| "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} " \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defensive formatting: if |
||
| "email address = '#{email_address}', " \ | ||
| "access role = '#{access_role}'. The invitation resource name is " \ | ||
| "#{response.result&.resource_name || 'N/A'}." | ||
| end | ||
| # [END invite_user_with_access_role] | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
| end | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
|
|
||
| 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 | ||
|
|
@@ -59,8 +85,8 @@ 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| | ||
| 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:' | ||
|
|
@@ -84,25 +110,41 @@ 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' || | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
| 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.\n\n" | ||
| puts parser | ||
| 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(:customer_id), | ||
| options.fetch(:email_address), | ||
| options.fetch(:access_role).to_sym, | ||
| options.fetch(:access_role), | ||
| ) | ||
| rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
|
@@ -26,15 +26,27 @@ | |
| require 'optparse' | ||
| require 'google/ads/google_ads' | ||
|
|
||
| ACCESS_ROLES = %w[ | ||
| ADMIN | ||
| STANDARD | ||
| READ_ONLY | ||
| EMAIL_ONLY | ||
| ].freeze | ||
|
|
||
| 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 | ||
| client = Google::Ads::GoogleAds::GoogleAdsClient.new | ||
|
|
||
| if !ACCESS_ROLES.include?(access_role) | ||
| raise "Illegal access role specified. Expected one of " \ | ||
| "#{ACCESS_ROLES.join(" ")}" | ||
| # 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 ArgumentError, "Illegal access role specified. Expected one of " \ | ||
| "#{ACCESS_ROLES.join(' ')}" | ||
| end | ||
| access_role = access_role_str.to_sym | ||
|
|
||
| user_id = get_user_id(client, customer_id, email_address) | ||
|
|
||
|
|
@@ -52,14 +64,15 @@ 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( | ||
| customer_id: customer_id, | ||
| query: query, | ||
| ) | ||
|
|
||
| result = response.first.customer_user_access | ||
| result = response.first&.customer_user_access | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optimization suggestion: in |
||
| if result | ||
| puts "Customer user access with User ID #{result.user_id}, Access Role " \ | ||
| "#{result.access_role}, Creation Time #{result.access_creation_date_time}, " \ | ||
|
|
@@ -75,26 +88,27 @@ 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( | ||
| customer_id: customer_id, | ||
| operation: operation, | ||
| ) | ||
|
|
||
| puts "Successfully updated customer user access with resource name " \ | ||
| "#{response.result.resource_name}." | ||
| 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 " \ | ||
| "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." | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
| else | ||
| puts "Successfully updated customer user access with resource name " \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defensive formatting: if |
||
| "#{response.result&.resource_name || 'N/A'}." | ||
| end | ||
| end | ||
|
|
||
| if __FILE__ == $0 | ||
| 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 | ||
|
|
@@ -108,7 +122,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 '' | ||
|
|
@@ -118,11 +132,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 | ||
|
|
||
|
|
@@ -133,25 +147,41 @@ 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' || | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
| 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.\n\n" | ||
| puts parser | ||
| 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 | ||
|
dorasun marked this conversation as resolved.
Resolved
|
||
| update_user_access( | ||
| options.fetch(:customer_id).tr("-", ""), | ||
| options.fetch(:customer_id), | ||
| options.fetch(:email_address), | ||
| 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", | ||
| 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 | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.