googleads / google-api-ads-ruby

Ad Manager SOAP API Client Libraries for Ruby
297 stars 227 forks source link

AdsCommon::Errors::Error Environment 'PRODUCTION' does not support version 'v201609 - which is the latest version. #115

Closed manjushah closed 7 years ago

manjushah commented 7 years ago

Here are the steps I followed.

  1. Installed latest version of google adwords api gem(using a ruby gem).

  2. Google ads common gem is using 0.12.5 version which is also updated one.

  3. In one of the docs on github it is mentioned if ruby version is lower than 2.2.2 then run gem install rack -v 1.6.4.

  4. All this was done and tested on development environment also when deployed to staging I had no issue with the environment.

  5. I was able to refresh the token and use the authentication hash to create google adwords api and create audiences.

  6. When deployed to production. I get this AdsCommon::Errors::Error Environment 'PRODUCTION' does not support version 'v201609 - which is the latest version.

Kindly help me understand what is the issue here and how do I resolve this

araishikeiwai commented 7 years ago

I experienced this too in production server. No problem in local machine before, but now the problem exists in local machine too.

google-adwords-api-0.22.0 google-ads-common-0.12.5

mcloonan commented 7 years ago

I apologize I missed this at first. Can you please run gem list on the affected environment and let me know the output. Maybe you have multiple versions of the google-adwords-api gem installed and it's using the wrong one. I just double-checked that I can make requests to v201609 using google-adwords-api version 0.22.0.

Also, the install of rack is only required if you can't install the google-ads-common gem normally. If you've already installed google-ads-common then rack is installed fine and there's no need to install a specific version separately.

araishikeiwai commented 7 years ago

here are my related gems installed

*** LOCAL GEMS ***

akami (1.3.1)
builder (3.2.2)
google-ads-common (0.12.5)
google-ads-savon (1.0.1)
google-adwords-api (0.22.0)
gyoku (1.3.1)
httpclient (2.8.2.4, 2.7.1)
httpi (2.4.2, 2.4.1)
nokogiri (1.6.8.1, 1.6.8, 1.6.7.2, 1.6.6.4)
nori (2.6.0)
signet (0.7.3, 0.5.1)
wasabi (3.5.0)
dklimkin commented 7 years ago

hi Rick, is this the output for the same user as the one your application runs as? Any chance you have multiple GEM_HOME's?

araishikeiwai commented 7 years ago

@dklimkin yes, i ran the gem list from the project directory (with its own gemset).

echo $GEM_HOME
> /home/araishikeiwai/.rvm/gems/ruby-2.3.0@projectname
bundle show google-adwords-api
> /home/araishikeiwai/.rvm/gems/ruby-2.3.0@projectname/gems/google-adwords-api-0.22.0

The code running the API is within the project.

dklimkin commented 7 years ago

Any chance you can log the library version on your app startup to confirm? Version is available as:

AdwordsApi::ApiConfig::CLIENT_LIB_VERSION

araishikeiwai commented 7 years ago
[1] pry(main)> AdwordsApi::ApiConfig::CLIENT_LIB_VERSION
=> "0.22.0"
dklimkin commented 7 years ago

Can you please share the line of the code which produce the error and exact error message?

araishikeiwai commented 7 years ago

here's the error stack:

AdsCommon::Errors::Error: Environment '' does not support version 'v201609'
/home/araishikeiwai/.rvm/gems/ruby-2.3.0@project_name/gems/google-ads-common-0.12.5/lib/ads_common/api.rb:156:in `validate_service_request'
/home/araishikeiwai/.rvm/gems/ruby-2.3.0@project_name/gems/google-ads-common-0.12.5/lib/ads_common/api.rb:76:in `service'
/home/araishikeiwai/project_folder/project_repo/app/services/adwords_service/upload_offline_conversions.rb:17:in `perform'
/home/araishikeiwai/project_folder/project_repo/app/services/services.rb:32:in `block in call'
/home/araishikeiwai/.rvm/gems/ruby-2.3.0@project_name/gems/activesupport-4.2.7.1/lib/active_support/callbacks.rb:88:in `__run_callbacks__'
/home/araishikeiwai/.rvm/gems/ruby-2.3.0@project_name/gems/activesupport-4.2.7.1/lib/active_support/callbacks.rb:778:in `_run_perform_callbacks'
/home/araishikeiwai/.rvm/gems/ruby-2.3.0@project_name/gems/activesupport-4.2.7.1/lib/active_support/callbacks.rb:81:in `run_callbacks'
/home/araishikeiwai/project_folder/project_repo/app/services/services.rb:31:in `call'
/home/araishikeiwai/project_folder/project_repo/app/services/adwords_service.rb:4:in `upload_offline_conversions'
/home/araishikeiwai/project_folder/project_repo/lib/tasks/adwords.rake:6:in `block (3 levels) in <top (required)>'

The code for upload_offline_conversions.rb follow the example given in https://developers.google.com/adwords/api/docs/samples/ruby/remarketing#upload-offline-conversions

require 'adwords_api'

module AdwordsService
  class UploadOfflineConversions < Services::Base
    API_VERSION = :v201609

    def initialize(gclid: nil, conversion_time: nil, conversion_value: nil)
      @conversion_name = 'Confirmed Offline Conversions'
      @gclid = gclid
      @conversion_time = conversion_time.strftime('%Y%m%d %H%M%S %z')
      @conversion_value = conversion_value.to_f
    end

    def perform
      config_filename = File.join(Rails.root, 'config', 'adwords_api.yml')
      adwords = AdwordsApi::Api.new(config_filename)
      conversion_feed_srv = adwords.service(:OfflineConversionFeedService, API_VERSION)

      feed = {
        conversion_name: @conversion_name,
        google_click_id: @gclid,
        conversion_time: @conversion_time,
        conversion_value: @conversion_value
      }

      return_feeds = conversion_feed_srv.mutate([{
        operator: 'ADD',
        operand: feed
      }])

      return_feeds[:value].each do |return_feed|
        log("Uploaded offline conversion value %.2f for Google Click ID '%s', to %s" % [return_feed[:conversion_value], return_feed[:google_click_id], return_feed[:conversion_name]])
      end
    rescue AdsCommon::Errors::OAuth2VerificationRequired
      log('Authorization credentials are not valid. Edit adwords_api.yml for ' +
        'OAuth2 client ID and secret and run misc/setup_oauth2.rb example ' +
        "to retrieve and store OAuth2 tokens.\n\n" +
        "See this wiki page for more details:\n\n  " +
        'https://github.com/googleads/google-api-ads-ruby/wiki/OAuth2')
      raise
    rescue AdsCommon::Errors::HttpError => e
      log("HTTP Error: %s" % e)
      raise
    rescue AdwordsApi::Errors::ApiException => e
      message = []
      message << "Message: %s" % e.message
      message << 'Errors:'
      e.errors.each_with_index do |error, index|
        message << "\tError [%d]:" % (index + 1)
        error.each do |field, value|
          message << "\t\t%s: %s" % [field, value]
        end
      end
      log(message.join("\n"))
      raise
    end

    def log(message)
      Rails.logger.info(tags: %w(adwords_upload_offline_conversions), message: message)
    end
  end
end
dklimkin commented 7 years ago

Hi Rick, this looks like a slightly different issue than the first post. You are missing to set up environment:

AdsCommon::Errors::Error: Environment '' does not support version 'v201609'

Do you have the following lines in your configuration file?

:service:
  :environment: PRODUCTION
araishikeiwai commented 7 years ago

I do

dklimkin commented 7 years ago

Could you please send me the configuration file referred here, over email?

config_filename = File.join(Rails.root, 'config', 'adwords_api.yml')

Please make sure to mask refresh/access tokens and OAuth secrets.

araishikeiwai commented 7 years ago

sorry, it looks like there's a mistake on my config file.

At first, I used the syntax like the given example:

:service:
  :environment: PRODUCTION

But I got

AdwordsApi::Errors::BadCredentialsError: Invalid client customer ID: <%= ENV['ADWORDS_CLIENT_CUSTOMER_ID'] %>

because the yml can't seem to parse the <%= %> and read the environment variable.

So I changed the syntax to

service:
  environment: PRODUCTION

And then got the error I mentioned.

Do you have any suggestion on how I can put environment variable in the config file?

dklimkin commented 7 years ago

This is not a supported flow. But you can set the client customer ID on runtime:

adwords = AdwordsApi::Api.new(config_filename)
adwords.credential_handler.set_credential(:client_customer_id, ENV[ADWORDS_CLIENT_CUSTOMER_ID])
araishikeiwai commented 7 years ago
  :oauth2_client_id:        <%= ENV['ADWORDS_API_OAUTH2_CLIENT_ID'] %>
  :oauth2_client_secret:    <%= ENV['ADWORDS_API_OAUTH2_CLIENT_SECRET'] %>
  :developer_token:         <%= ENV['ADWORDS_DEVELOPER_TOKEN'] %>
  :client_customer_id:      <%= ENV['ADWORDS_CLIENT_CUSTOMER_ID'] %>
  :oauth2_token:
    :access_token:          <%= ENV['ADWORDS_OAUTH2_ACCESS_TOKEN'] %>
    :refresh_token:         <%= ENV['ADWORDS_OAUTH2_REFRESH_TOKEN'] %>
    :issued_at:             <%= ENV['ADWORDS_OAUTH2_ISSUED_AT'] %>

those are the variables I put in env. I moved them to the Ruby code:

      adwords = AdwordsApi::Api.new(config_filename)
      adwords.credential_handler.set_credential(:oauth2_client_id, ENV['ADWORDS_API_OAUTH2_CLIENT_ID'])
      adwords.credential_handler.set_credential(:oauth2_client_secret, ENV['ADWORDS_API_OAUTH2_CLIENT_SECRET'])
      adwords.credential_handler.set_credential(:developer_token, ENV['ADWORDS_DEVELOPER_TOKEN'])
      adwords.credential_handler.set_credential(:client_customer_id, ENV['ADWORDS_CLIENT_CUSTOMER_ID'])

      oauth2_token_hash = {
        access_token: ENV['ADWORDS_OAUTH2_ACCESS_TOKEN'],
        refresh_token: ENV['ADWORDS_OAUTH2_REFRESH_TOKEN'],
        issued_at: ENV['ADWORDS_OAUTH2_ISSUED_AT']
      }
      adwords.credential_handler.set_credential(:oauth2_token, oauth2_token_hash)

Now I get

AdwordsApi::V201609::OfflineConversionFeedService::ApiException: [AuthenticationError.OAUTH_TOKEN_INVALID @ ; trigger:'<null>']

Looks like I'm almost there. Should I do over the oauth2 process?

manjushah commented 7 years ago

Hi ,

I solved this problem.

Issue was when I create the AdWords object with all the token information I was not sending a proper json format that was the issue.

Thanks for the reply.

On 7 Dec 2016 2:32 p.m., "Rick Daniel" notifications@github.com wrote:

I experienced this too in production server. No problem in local machine before, but now the problem exists.

google-adwords-api-0.22.0 google-ads-common-0.12.5

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/googleads/google-api-ads-ruby/issues/115#issuecomment-265392846, or mute the thread https://github.com/notifications/unsubscribe-auth/AHolq8zJfEIbrqLNUcVOyBqexDDEFO0Dks5rFnYbgaJpZM4K31IC .

araishikeiwai commented 7 years ago

Hi @dklimkin thank you, I've re-setup the oauth2 and got it working now.

Other question about the oauth2 (one that i'm not really well-versed at), will I need to manually refresh my token again in the future?

dklimkin commented 7 years ago

@manjushah, good to hear this is resolved.

Rick, refresh token is valid until revoked and the library will take care of access token refresh, so no need for manual changes. It's recommended to share the API object for consecutive calls so access token is not refreshed to aggressively though.

araishikeiwai commented 7 years ago

thank you very much for your helps @dklimkin

araishikeiwai commented 7 years ago

@dklimkin sorry for bugging you again for this matter.

sadly when the cron job for the reporting started (at 1 am every night), I got OAUTH_TOKEN_INVALID. then in the afternoon I reran the oauth2_setup to get another token, and ran the reporting and succeeded. however, at 1 am last night I got yet another OAUTH_TOKEN_INVALID.

do you have an idea why this might happen?

P.S.: I ran the service each for every conversion, so the API object is created once for each conversion reported. I wonder if this was the cause so I have to modify the service to reuse the same API object

araishikeiwai commented 7 years ago

I modified the code to reuse the API object. While doing that, I found out that I forgot to put the expires_in key into the oauth2_token hash (which may be the cause for the invalid token for the next calls). Will let you know what happens later.