-
Notifications
You must be signed in to change notification settings - Fork 73
Add incentives examples in Ruby #542
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
Changes from all commits
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 |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| #!/usr/bin/env ruby | ||
|
|
||
| # | ||
| # Copyright 2025 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 applies an incentive to a user's account. | ||
| # | ||
| # This example is a no-op if the user already has an accepted incentive. If the | ||
| # user attempts to apply a new incentive, the response will simply return the | ||
| # existing incentive that has already been applied to the account. Use the | ||
| # fetch_incentives.rb example to get the available incentives. | ||
|
|
||
| require "optparse" | ||
| require "google/ads/google_ads" | ||
|
|
||
| # [START apply_incentive] | ||
| def apply_incentive(customer_id, incentive_id, country_code) | ||
| # GoogleAdsClient will read a config file from | ||
| # ENV['HOME']/google_ads_config.rb when called without parameters | ||
| client = Google::Ads::GoogleAds::GoogleAdsClient.new | ||
|
|
||
| request_params = { | ||
| customer_id: customer_id, | ||
| selected_incentive_id: incentive_id | ||
| } | ||
|
|
||
| if country_code | ||
| request_params[:country_code] = country_code | ||
| end | ||
|
|
||
| response = client.service.incentive.apply_incentive(request_params) | ||
|
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. Idempotency Check: Since the API returns the existing incentive if one is already applied, add a check here to tell the user whether this was a new application or a retrieval of an existing one to prevent confusion. |
||
|
|
||
| puts "Applied incentive." | ||
|
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. Conditional Feedback: The message 'Applied incentive' might be misleading if the user already had one (as per the header comments). Change this to 'Incentive details retrieved/applied' for better accuracy. |
||
| puts "Coupon Code: #{response.coupon_code}" | ||
| puts "Creation Time: #{response.creation_time}" | ||
|
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. Date Formatting: The raw creation time might be a timestamp object. Formatting this (e.g., .strftime('%Y-%m-%d %H:%M:%S')) makes the console output much more readable. |
||
| end | ||
| # [END apply_incentive] | ||
|
|
||
| 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 | ||
| # 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" | ||
|
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. Safety Guard: Implement a check to ensure INSERT_ placeholders are replaced. Running this with dummy strings results in a wasted API call and a 400-range error. |
||
| options[:incentive_id] = "INSERT_INCENTIVE_ID_HERE" | ||
|
|
||
| OptionParser.new do |opts| | ||
| opts.banner = sprintf("Usage: %s [options]", File.basename(__FILE__)) | ||
|
|
||
| opts.separator "" | ||
| opts.separator "Options:" | ||
|
|
||
| opts.on("-c", "--customer-id CUSTOMER-ID", String, "The Google Ads customer ID") do |v| | ||
| options[:customer_id] = v | ||
| end | ||
|
|
||
| opts.on("-i", "--incentive-id INCENTIVE-ID", Integer, "The incentive ID to select") do |v| | ||
| options[:incentive_id] = v | ||
| end | ||
|
|
||
| opts.on("-k", "--country-code COUNTRY-CODE", String, "The country code of the user (e.g. 'US')") do |v| | ||
| options[:country_code] = v | ||
| end | ||
|
|
||
| opts.separator "" | ||
| opts.separator "Help:" | ||
|
|
||
| opts.on_tail("-h", "--help", "Show this message") do | ||
| puts opts | ||
| exit | ||
| end | ||
| end.parse! | ||
|
|
||
| begin | ||
| apply_incentive( | ||
| options.fetch(:customer_id).tr("-", ""), | ||
|
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. Input Sanitation: Moving the .tr("-", "") logic into a helper method or the apply_incentive method itself ensures consistency if you ever call this function from a different part of the app. |
||
| options.fetch(:incentive_id), | ||
| options[:country_code] | ||
| ) | ||
| rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e | ||
| e.failure.errors.each do |error| | ||
| $stderr.printf("Error with message: %s\n", error.message) | ||
| error.location&.field_path_elements&.each do |field_path_element| | ||
| $stderr.printf("\tOn field: %s\n", field_path_element.field_name) | ||
| end | ||
| error.error_code.to_h.each do |k, v| | ||
| next if v == :UNSPECIFIED | ||
|
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. Detailed Logging: "In a production environment, you should log these errors to a persistent file (e.g., ads_api.log) rather than just printing to $stderr, which is lost after the terminal closes." |
||
| $stderr.printf("\tType: %s\n\tCode: %s\n", k, v) | ||
| end | ||
| end | ||
| raise | ||
|
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. Consider adding a comment to explain why the error is re-raised or apply the comment from fetch_incentives.rb. |
||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| #!/usr/bin/env ruby | ||
|
|
||
| # | ||
| # Copyright 2025 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 returns incentives for a given user. | ||
| # | ||
| # To apply an incentive, use apply_incentive.rb. | ||
|
|
||
| require "optparse" | ||
| require "google/ads/google_ads" | ||
|
|
||
| # [START fetch_incentives] | ||
| def fetch_incentives(email_address, language_code, country_code) | ||
|
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. Dependency Injection: Instead of instantiating the client inside the method, pass it as an argument. This allows you to share one authenticated session across multiple API calls, reducing overhead. |
||
| # GoogleAdsClient will read a config file from | ||
| # ENV['HOME']/google_ads_config.rb when called without parameters | ||
| client = Google::Ads::GoogleAds::GoogleAdsClient.new | ||
|
|
||
| response = client.service.incentive.fetch_incentive( | ||
| email: email_address, | ||
| language_code: language_code, | ||
| country_code: country_code | ||
| ) | ||
|
|
||
| if response.incentive_offer&.cyo_incentives | ||
| puts "Fetched incentive." | ||
| # If the offer type is CHOOSE_YOUR_OWN_INCENTIVE, there will be three | ||
| # incentives in the response. At the time this example was written, all | ||
| # incentive offers are CYO incentive offers. | ||
| cyo_incentives = response.incentive_offer.cyo_incentives | ||
| puts cyo_incentives.low_offer.inspect | ||
|
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. Human-Readable Output: Replace .inspect with a helper method to print specific attributes like offer_id or expiration_date. Raw object inspection is difficult for end-users to parse." |
||
| puts cyo_incentives.medium_offer.inspect | ||
| puts cyo_incentives.high_offer.inspect | ||
| else | ||
| puts "No incentives found." | ||
| end | ||
| end | ||
| # [END fetch_incentives] | ||
|
|
||
| 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 | ||
| # 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[:email_address] = "INSERT_EMAIL_ADDRESS_HERE" | ||
|
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. Placeholder Check: Add a check to ensure the user actually replaced INSERT_EMAIL_ADDRESS_HERE. You could raise an error immediately if the string still contains 'INSERT' to prevent an unnecessary API round-trip. |
||
| options[:language_code] = "en" | ||
| options[:country_code] = "US" | ||
|
|
||
| OptionParser.new do |opts| | ||
| opts.banner = sprintf("Usage: %s [options]", File.basename(__FILE__)) | ||
|
|
||
| opts.separator "" | ||
| opts.separator "Options:" | ||
|
|
||
| opts.on("-e", "--email-address EMAIL-ADDRESS", String, "The email of the user to fetch incentives for") do |v| | ||
| options[:email_address] = v | ||
| end | ||
|
|
||
| opts.on("-l", "--language-code LANGUAGE-CODE", String, "The language code of the user (e.g. 'en')") do |v| | ||
| options[:language_code] = v | ||
| end | ||
|
|
||
| opts.on("-k", "--country-code COUNTRY-CODE", String, "The country code of the user (e.g. 'US')") do |v| | ||
| options[:country_code] = v | ||
| end | ||
|
|
||
| opts.separator "" | ||
| opts.separator "Help:" | ||
|
|
||
| opts.on_tail("-h", "--help", "Show this message") do | ||
| puts opts | ||
| exit | ||
| end | ||
| end.parse! | ||
|
|
||
| begin | ||
| fetch_incentives( | ||
| options.fetch(:email_address), | ||
| options.fetch(:language_code), | ||
| options.fetch(:country_code) | ||
| ) | ||
| rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e | ||
| e.failure.errors.each do |error| | ||
| $stderr.printf("Error with message: %s\n", error.message) | ||
| error.location&.field_path_elements&.each do |field_path_element| | ||
| $stderr.printf("\tOn field: %s\n", field_path_element.field_name) | ||
| 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 | ||
|
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. Error Bubbling: Instead of re-raising the raw GoogleAdsError, consider wrapping it in a domain-specific error (e.g., IncentiveFetchError). This makes it easier for higher-level services to catch and handle logic errors separately from network errors. |
||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource Optimization: Instantiating the client inside the method is expensive for repetitive calls. Consider passing a shared client instance to allow for connection pooling.