google-ads-common 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
4
+ #
5
+ # Copyright:: Copyright 2010, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Configuration handler, can load data from a YAML configuration file and
21
+ # rewrite it in memory.
22
+
23
+ require 'rubygems'
24
+ require 'yaml'
25
+
26
+ module AdsCommon
27
+ class Config
28
+
29
+ # Initialized the Config object with either the contents of a provided file
30
+ # or a provided hash.
31
+ def initialize(param = nil)
32
+ @config = {}
33
+ if param and param.is_a? String
34
+ load(param)
35
+ elsif param and param.is_a? Hash
36
+ set_all(param)
37
+ end
38
+ end
39
+
40
+ # Reads a property or category from the loaded configuration.
41
+ # They can be indexed using a dot-based notation (e.g. "category.property"
42
+ # to access "property" under "category").
43
+ def read(property_path = nil)
44
+ current_level = @config
45
+ if property_path
46
+ property_path.split('.').each do |item|
47
+ return nil if !current_level or !(current_level.is_a?(Hash))
48
+ current_level = current_level[item.to_sym]
49
+ end
50
+ end
51
+ return current_level
52
+ end
53
+
54
+ # Writes a new value to a property or category in memory (creating it if
55
+ # necessary).
56
+ # They can be indexed using a dot-based notation (e.g. "category.property"
57
+ # to access "property" under "category").
58
+ def set(property_path, value)
59
+ @config = {} if !@config
60
+ current_level = @config
61
+ if property_path
62
+ segments = property_path.split('.')
63
+ segments[0,-2].each do |item|
64
+ return nil if !current_level or !(current_level.is_a?(Hash))
65
+ current_level = current_level[item.to_sym]
66
+ end
67
+ if current_level and current_level.is_a?(Hash)
68
+ current_level[segments.last] = value
69
+ return value
70
+ end
71
+ end
72
+ return nil
73
+ end
74
+
75
+ # Writes an entire set of properties.
76
+ def set_all(properties)
77
+ @config = process_hash_keys(properties)
78
+ end
79
+
80
+ # Auxiliary method to recurse through a hash and convert all the keys to
81
+ # symbols.
82
+ def process_hash_keys(hash)
83
+ new_hash = {}
84
+ hash.each do |key, value|
85
+ if value.is_a? Hash
86
+ new_hash[key.to_sym] = process_hash_keys(value)
87
+ else
88
+ new_hash[key.to_sym] = value
89
+ end
90
+ end
91
+ return new_hash
92
+ end
93
+
94
+ # Reads a configuration file and returns a Ruby structure with the complete
95
+ # set of keys and values.
96
+ #
97
+ #
98
+ # Args:
99
+ # - filename: config file to be read (*String*)
100
+ #
101
+ # Returns:
102
+ # - Ruby objects for the stored YAML
103
+ #
104
+ # Raises:
105
+ # - <b>Errno::EACCES</b> if the file does not exist.
106
+ #
107
+ def load(filename)
108
+ unless File.exist?(filename)
109
+ raise(Errno::EACCES, "File #{filename} does not exist")
110
+ end
111
+ file = nil
112
+ begin
113
+ file = File.open(filename)
114
+ @config = YAML::load(file)
115
+ ensure
116
+ file.close if file
117
+ end
118
+ return @config
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
4
+ #
5
+ # Copyright:: Copyright 2010, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Generic class to handle credentials across client libraries.
21
+
22
+ module AdsCommon
23
+ class CredentialHandler
24
+
25
+ def initialize(config)
26
+ @config = config
27
+ load_from_config
28
+ end
29
+
30
+ # Retrieve all credentials (optional parameter specifying API version).
31
+ def credentials(version = nil)
32
+ return @credentials
33
+ end
34
+
35
+ # Set the credentials hash to a new one. Calculate difference, and call the
36
+ # AuthHandler callback appropriately.
37
+ def credentials=(new_credentials)
38
+ # Find new and changed properties.
39
+ diff = new_credentials.select do |key, value|
40
+ value != @credentials[key]
41
+ end
42
+
43
+ # Find removed properties.
44
+ removed = @credentials.select do |key, value|
45
+ new_credentials[key] == nil
46
+ end
47
+ if removed
48
+ removed = removed.map { |entry| [entry[0], nil] }
49
+ diff += removed
50
+ end
51
+
52
+ # Set each property.
53
+ diff.each do |entry|
54
+ set_credential(entry[0], entry[1])
55
+ end
56
+ end
57
+
58
+ # Set a single credential to a new value. Call the AuthHandler callback
59
+ # appropriately.
60
+ def set_credential(credential, value)
61
+ @credentials[credential] = value
62
+ @auth_handler.property_changed(credential, value) if auth_handler
63
+ end
64
+
65
+ private
66
+
67
+ # Loads the credentials from the config data.
68
+ def load_from_config
69
+ @credentials = @config.read('authentication')
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Contains common errors across APIs, as well as base classes to inherit from
21
+ # in specific APIs.
22
+
23
+ module AdsCommon
24
+ module Errors
25
+
26
+ # Generic error class for non-specific errors.
27
+ class Error < ::StandardError
28
+ end
29
+
30
+ # Raised if an attempt is made to authenticate via the ClientLogin API with
31
+ # missing or wrong information
32
+ class AuthError < Error
33
+ end
34
+
35
+ # Raised if setting a non-existant property on an object
36
+ class MissingPropertyError < Error
37
+ attr_reader :property, :object_type
38
+ def initialize(property, object_type)
39
+ @property = property
40
+ @object_type = object_type
41
+ end
42
+ end
43
+
44
+ # Raised if an attempt is made to connect to the sandbox with production
45
+ # credentials or vice-versa
46
+ class EnvironmentMismatchError < Error
47
+ end
48
+
49
+ # Raised if a HTTP error like 403 or 404 has happened.
50
+ class HttpError < Error
51
+ end
52
+
53
+ # Raised if an error has occured during code generation.
54
+ class BuildError < Error
55
+ end
56
+
57
+ # Superclass for API exceptions. Each client library should implement its
58
+ # own subclass with extra fields.
59
+ class ApiException < Error
60
+
61
+ attr_reader :message
62
+
63
+ def initialize(message = nil)
64
+ @message = message
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Contains helpful methods to make HTTP requests, using all of the connection
21
+ # options available through the client library.
22
+
23
+ require 'httpi'
24
+
25
+ require 'ads_common/errors'
26
+
27
+ module AdsCommon
28
+ module Http
29
+
30
+ # Performs a get on a URL, using all of the connection options in the
31
+ # client library, returning a HTTPI::Response.
32
+ def self.get_response(url, config = nil, headers = nil)
33
+ request = prepare_request(config, url, headers)
34
+ response = HTTPI.get(request)
35
+ return response
36
+ end
37
+
38
+ # Performs a get on a URL, using all of the connection options in the
39
+ # client library, returning the response body as a string.
40
+ def self.get(url, config = nil, headers = nil)
41
+ return get_response(url, config, headers).body
42
+ end
43
+
44
+ # Performs a post on a URL, using all of the connection options in the
45
+ # client library, returning a HTTPI::Response.
46
+ def self.post_response(url, data, config = nil, headers = nil)
47
+ request = prepare_request(config, url, headers, data)
48
+ response = HTTPI.post(request)
49
+ return response
50
+ end
51
+
52
+ # Performs a post on a URL, using all of the connection options in the
53
+ # client library, returning the response body as a string.
54
+ def self.post(url, data, config = nil, headers = nil)
55
+ return post_response(url, data, config, headers).body
56
+ end
57
+
58
+ private
59
+
60
+ # Returns a suitably configured request object for a given URL and options.
61
+ # Defaulting to stricter :peer validation.
62
+ def self.prepare_request(config, url, headers = nil, data = nil)
63
+ request = HTTPI::Request.new(url)
64
+ proxy = config ? config.read('connection.proxy') : nil
65
+ request.proxy = proxy if proxy
66
+ strict_ssl = config.nil? or
67
+ !(config.read('connection.strict_ssl_verification') == 'false')
68
+ request.auth.ssl.verify_mode = strict_ssl ? :peer : :none
69
+ request.headers = headers if headers
70
+ request.body = data if data
71
+ HTTPI.log = false # TODO remove after logger CL.
72
+ return request
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Author:: api.sgomes@gmail.com (Sérgio Gomes)
4
+ #
5
+ # Copyright:: Copyright 2010, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Wrapper class to handle logging to console and/or files.
21
+
22
+ require 'logger'
23
+
24
+ module AdsCommon
25
+
26
+ # Wrapper class to handle logging to console and/or files.
27
+ class Logger
28
+
29
+ # Constructor for AdsLogger.
30
+ #
31
+ # Args:
32
+ # - filename: the filename for the log file to be written (if log_to_file is
33
+ # called)
34
+ # - log_to_console: boolean, indicates whether or not to log to the console
35
+ #
36
+ def initialize(filename, log_to_console=false)
37
+ @filename = filename
38
+ @loggers = []
39
+ if log_to_console
40
+ stderr_logger = ::Logger.new(STDERR)
41
+ stderr_logger.level = ::Logger::INFO
42
+ @loggers << stderr_logger
43
+ end
44
+ end
45
+
46
+ # Enables logging to a file.
47
+ # May be called several times to log to multiple files.
48
+ #
49
+ # Args:
50
+ # - path: where to write the file (defaults to current dir). Path only, do
51
+ # not provide filename.
52
+ #
53
+ def log_to_file(path='.')
54
+ new_logger = ::Logger.new(File.join(path, @filename))
55
+ new_logger.level = ::Logger::INFO
56
+ @loggers << new_logger
57
+
58
+ return nil
59
+ end
60
+
61
+ # Overload << operator to perform logging.
62
+ def << (text)
63
+ @loggers.each do |logger|
64
+ logger.info text.to_s
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Handles SOAP headers and namespaces definition for Savon SOAP backend.
21
+
22
+ module AdsCommon
23
+ module SavonHeaders
24
+ class SimpleHeaderHandler
25
+
26
+ # Default namespace alias
27
+ DEFAULT_NAMESPACE = 'wsdl'
28
+
29
+ # Initializes a header handler.
30
+ #
31
+ # Args:
32
+ # - credential_handler: a header with credential data.
33
+ # - auth_handler: a header with auth data.
34
+ # - element_name: an API-specific name of header element.
35
+ # - namespace: default namespace to use.
36
+ # - version: services version.
37
+ def initialize(credential_handler, auth_handler, element_name,
38
+ namespace, version)
39
+ @credential_handler = credential_handler
40
+ @auth_handler = auth_handler
41
+ @element_name = element_name
42
+ @namespace = namespace
43
+ @version = version
44
+ Savon.configure {|config| config.raise_errors = false}
45
+ end
46
+
47
+ # Enriches soap object with API-specific headers like namespaces, login
48
+ # credentials etc.
49
+ #
50
+ # Args:
51
+ # - soap: a Savon soap object to fill fields in.
52
+ # - args: request parameters to adjust for namespaces.
53
+ #
54
+ # Returns:
55
+ # - Modified soap structure.
56
+ def prepare_soap(soap, args)
57
+ soap.header[prepend_namespace(@element_name)] =
58
+ generate_request_header()
59
+ soap.namespace = @namespace
60
+ if (!args.nil?)
61
+ soap.body = prepare_args(args)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Generates SOAP request header with login credentials and namespace
68
+ # definition.
69
+ #
70
+ # Args:
71
+ # - None.
72
+ #
73
+ # Returns:
74
+ # - Hash containing a header with filled in credentials.
75
+ def generate_request_header()
76
+ request_header = {}
77
+ credentials = @credential_handler.credentials(@version)
78
+ headers = @auth_handler.headers(credentials)
79
+ headers.each do |header, value|
80
+ request_header[prepend_namespace(header)] = value
81
+ end
82
+ return request_header
83
+ end
84
+
85
+ # Modifies request parameters to include namespace reference. For Hash
86
+ # and Array data types dives deeper into structure.
87
+ #
88
+ # Args:
89
+ # - args: subtree of request parameters.
90
+ #
91
+ # Returns:
92
+ # - subtree with modified parameters including namespaces.
93
+ def prepare_args(args)
94
+ res = case args
95
+ when Hash
96
+ prepare_hash_args(args)
97
+ when Array
98
+ prepare_array_args(args)
99
+ else
100
+ args
101
+ end
102
+ return res
103
+ end
104
+
105
+ # Crawls hash for all the request parameters and adds namespace
106
+ # reference for the keys.
107
+ #
108
+ # Args:
109
+ # - args: Hash element of subtree of request parameters.
110
+ #
111
+ # Returns:
112
+ # - Modified Hash with all keys updated and all values crawled.
113
+ def prepare_hash_args(args)
114
+ res = {}
115
+ args.each do |key, value|
116
+ res[prepend_namespace(key)] = prepare_args(value)
117
+ end
118
+ return res
119
+ end
120
+
121
+ # Crawls array and process each of its elements to include namespace.
122
+ #
123
+ # Args:
124
+ # - args: Array element of subtree of request parameters.
125
+ #
126
+ # Returns:
127
+ # - Modified Array with every element crawled.
128
+ def prepare_array_args(args)
129
+ res = []
130
+ args.each do |item|
131
+ res << prepare_args(item)
132
+ end
133
+ return res
134
+ end
135
+
136
+ # Adds namespace to the request parameter name.
137
+ #
138
+ # Args:
139
+ # - str: String to prepend with a namespace
140
+ #
141
+ # Returns:
142
+ # - String with a namespace.
143
+ def prepend_namespace(str)
144
+ return "%s:%s" % [DEFAULT_NAMESPACE, str.to_s]
145
+ end
146
+ end
147
+ end
148
+ end