google-ads-common 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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