Class: Adapi::Api

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Naming
Includes:
ActiveModel::Conversion, ActiveModel::Validations
Defined in:
lib/adapi/api.rb

Defined Under Namespace

Classes: AdGroupError, ApiError, CampaignError

Constant Summary collapse

LOGGER =
Config.setup_logger
API_EXCEPTIONS =
[
  AdsCommon::Errors::ApiException, 
  AdsCommon::Errors::HttpError, 
  AdwordsApi::Errors::ApiException
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Api

Returns a new instance of Api.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/adapi/api.rb', line 42

def initialize(params = {})
  params.symbolize_keys!

  raise "Missing Service Name" unless params[:service_name]

  @adwords = params[:adwords_api_instance]

  # REFACTOR
  unless @adwords
    @adwords = AdwordsApi::Api.new(Adapi::Config.read)

    authentication_method = Adapi::Config.read[:authentication][:method].to_s.upcase

    case authentication_method
    when "CLIENTLOGIN", "OAUTH"
      warn "#{authentication_method} is nearly obsolete, please update to OAuth2"
    when "OAUTH2_JWT"
      raise "OAUTH2_JWT is not yet implemented, please use OAUTH2 instead"
    # authorize to oauth2
    when "OAUTH2"
      oauth2_token = Adapi::Config.read[:authentication][:oauth2_token]

      if oauth2_token.nil? || oauth2_token.class != Hash 
        raise "Missing or invalid OAuth2 token"
      end

      @adwords.authorize({:oauth2_verification_code => $token})
    end
  end

  @adwords.logger = LOGGER if LOGGER
  @version = API_VERSION
  @service = @adwords.service(params[:service_name].to_sym, @version)
  @params = params
end

Instance Attribute Details

#adwordsObject

Returns the value of attribute adwords.



30
31
32
# File 'lib/adapi/api.rb', line 30

def adwords
  @adwords
end

#idObject

Returns the value of attribute id.



30
31
32
# File 'lib/adapi/api.rb', line 30

def id
  @id
end

#paramsObject

Returns the value of attribute params.



30
31
32
# File 'lib/adapi/api.rb', line 30

def params
  @params
end

#serviceObject

Returns the value of attribute service.



30
31
32
# File 'lib/adapi/api.rb', line 30

def service
  @service
end

#statusObject

Returns the value of attribute status.



30
31
32
# File 'lib/adapi/api.rb', line 30

def status
  @status
end

#versionObject

Returns the value of attribute version.



30
31
32
# File 'lib/adapi/api.rb', line 30

def version
  @version
end

#xsi_typeObject

Returns the value of attribute xsi_type.



30
31
32
# File 'lib/adapi/api.rb', line 30

def xsi_type
  @xsi_type
end

Class Method Details

.create(params = {}) ⇒ Object



103
104
105
106
107
108
109
110
# File 'lib/adapi/api.rb', line 103

def self.create(params = {})
  # FIXME deep symbolize_keys, probably through ActiveSupport
  params.symbolize_keys! if params.is_a?(Hash)

  api_instance = self.new(params)
  api_instance.create
  api_instance
end

.to_micro_units(x) ⇒ Object

convert number to micro units (unit * one million)



227
228
229
# File 'lib/adapi/api.rb', line 227

def self.to_micro_units(x)
  (x.to_f * 1e6).to_i
end

.update(params = {}) ⇒ Object

done mostly for campaign, probably won’t work pretty much anywhere else which can be easily fixed creating by self.update method for specific class



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/adapi/api.rb', line 116

def self.update(params = {})
  params.symbolize_keys!

  # PS: updating campaign without finding it is much faster
  api_instance = self.new()
  api_instance.id = params.delete(:id)
  api_instance.errors.add('id', 'is missing') unless api_instance.id
  
  api_instance.update(params)
  api_instance
end

Instance Method Details

#[](k) ⇒ Object

FIXME hotfix, should be able to sort it out better through ActiveModel



87
88
89
# File 'lib/adapi/api.rb', line 87

def [](k)
  self.send(k)
end

#[]=(k, v) ⇒ Object



91
92
93
# File 'lib/adapi/api.rb', line 91

def []=(k,v)
  self.send("#{k}=", v)
end

#attributesObject Also known as: to_hash

Returns hash of attributes for a model instance

This is an implementation of ActiveRecord::Base#attributes method. Children of API model customize this method for their own attributes.



38
39
40
# File 'lib/adapi/api.rb', line 38

def attributes
  { status: status, xsi_type: xsi_type }
end

#check_for_errors(adapi_instance, options = {}) ⇒ Object

Deals with campaign exceptions encountered during complex operations over AdWords API

Parameters: store_errors (default: true) - add errors to self.error collection raise_errors (default: false) - raises exception CampaignError (after optional saving errors)



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/adapi/api.rb', line 186

def check_for_errors(adapi_instance, options = {})
  options.merge!( store_errors: true, raise_errors: false )

  # don't store errors in this case, because errors are already there
  # and loop in store_errors method would cause application to hang
  options[:store_errors] = false if (adapi_instance == self)

  unless adapi_instance.errors.empty?
    store_errors(adapi_instance, options[:prefix]) if options[:store_errors]

    if options[:raise_errors]
      exception_type = case adapi_instance.xsi_type
        when "Campaign" then CampaignError
        when "AdGroup" then AdGroupError
        else ApiError
      end

      raise exception_type
    end
  end
end

#mutate(operation) ⇒ Object

wrap AdWords add/update/destroy actions and deals with errors PS: Keyword and Ad models have their own wrappers because of PolicyViolations



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/adapi/api.rb', line 132

def mutate(operation)      
  operation = [operation] unless operation.is_a?(Array)
  
  # fix to save space during specifyng operations
  operation = operation.map do |op|
    op[:operand].delete(:status) if op[:operand][:status].nil?
    op
  end
  
  begin

    response = @service.mutate(operation)

  rescue *API_EXCEPTIONS => e

    unless e.respond_to?(:errors)
      self.errors.add(:base, e.message)
      return false
    end

    e.errors.each do |error|
      if (error[:xsi_type] == 'PolicyViolationError') || (error[:api_error_type] == 'PolicyViolationError')
        # return exemptable PolicyViolations errors in custom format so we can request exemptions
        # see adwords-api gem example for details: handle_policy_violation_error.rb
        # so far, this applies only for keywords and ads
        if error[:is_exemptable]
          self.errors.add( :PolicyViolationError, error[:key].merge(
            :operation_index => AdwordsApi::Utils.operation_index_for_error(error)
          ) )
        end

        # besides PolicyViolations errors in custom format, return all errors also in regular format
        self.errors.add(:base, "violated %s policy: \"%s\" on \"%s\"" % [
          error[:is_exemptable] ? 'exemptable' : 'non-exemptable', 
          error[:key][:policy_name], 
          error[:key][:violating_text]
        ])
      else
        self.errors.add(:base, e.message)
      end
    end # of errors.each

    false
  end
  
  response
end

#new?Boolean

detects whether the instance has been saved already

Returns:

  • (Boolean)


99
100
101
# File 'lib/adapi/api.rb', line 99

def new?
  self.id.blank?
end

#persisted?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/adapi/api.rb', line 82

def persisted?
  false
end

#store_errors(failed_instance, error_prefix = nil) ⇒ Object

Shortcut for pattern used in Campaign#update method When partial update fails, store errors in main campaign instance



211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/adapi/api.rb', line 211

def store_errors(failed_instance, error_prefix = nil)
  raise "#{failed_instance.xsi_type}#store_errors: Invalid object instance" unless failed_instance.respond_to?(:errors)

  error_prefix ||= failed_instance.respond_to?(:xsi_type) ? failed_instance.xsi_type : nil

  failed_instance.errors.messages.each_pair do |k, v|
      k = "#{error_prefix}::#{k}" if error_prefix and (k != :base)

      Array(v).each do |x| 
        self.errors.add(k, x)
      end
  end
end

#to_paramObject



78
79
80
# File 'lib/adapi/api.rb', line 78

def to_param
  self[:id]
end