sem4r 0.1.2 → 0.1.3

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.
Files changed (175) hide show
  1. data/Gemfile +13 -2
  2. data/Gemfile.lock +13 -7
  3. data/README.rdoc +44 -7
  4. data/Rakefile +2 -122
  5. data/VERSION.yml +1 -1
  6. data/bin/sem +1 -0
  7. data/examples_sem4r/01_get_account.rb +1 -0
  8. data/examples_sem4r/02_get_info.rb +1 -0
  9. data/examples_sem4r/03_list_ad.rb +1 -0
  10. data/examples_sem4r/04_list_keywords.rb +1 -0
  11. data/examples_sem4r/05_request_report.rb +25 -14
  12. data/examples_sem4r/{05_request_report_2010.rb → 06_request_report_definition.rb} +2 -4
  13. data/examples_sem4r/{06_create_campaigns.rb → 07_create_campaigns.rb} +1 -0
  14. data/examples_sem4r/{07_create_campaigns_block.rb → 08_create_campaigns_block.rb} +1 -0
  15. data/examples_sem4r/{07_create_campaigns_simple.rb → 09_create_campaigns_simple.rb} +1 -0
  16. data/examples_sem4r/{08_ad_params.rb → 10_ad_params.rb} +1 -0
  17. data/examples_sem4r/{09_targeting_idea.rb → 11_targeting_idea.rb} +1 -0
  18. data/examples_sem4r/{10_get_location.rb → 12_get_location.rb} +1 -0
  19. data/examples_sem4r/{11_submit_bulk_job.rb → 13_submit_bulk_job.rb} +1 -1
  20. data/examples_sem4r/{12_list_bulk_job.rb → 14_list_bulk_job.rb} +3 -0
  21. data/examples_sem4r/30_prune_empty_adgroup.rb +1 -0
  22. data/examples_sem4r/31_empty_accounts.rb +1 -0
  23. data/examples_sem4r/example_helper.rb +2 -1
  24. data/lib/sem4r/ad_group/ad_group.rb +22 -7
  25. data/lib/sem4r/ad_group/ad_group_bids.rb +1 -1
  26. data/lib/sem4r/ad_group/ad_group_service.rb +12 -5
  27. data/lib/sem4r/ad_group_ad/ad_group_ad.rb +1 -0
  28. data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +1 -0
  29. data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +14 -9
  30. data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +16 -25
  31. data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +18 -14
  32. data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +27 -19
  33. data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +2 -1
  34. data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +1 -0
  35. data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +9 -4
  36. data/lib/sem4r/ad_group_criterion/criterion.rb +1 -0
  37. data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +1 -0
  38. data/lib/sem4r/ad_group_criterion/criterion_placement.rb +1 -0
  39. data/lib/sem4r/ad_param/ad_param.rb +24 -8
  40. data/lib/sem4r/ad_param/ad_param_service.rb +8 -5
  41. data/lib/sem4r/adwords.rb +232 -76
  42. data/lib/sem4r/base.rb +1 -1
  43. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +41 -35
  44. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_account_extension.rb +49 -15
  45. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +29 -20
  46. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +13 -14
  47. data/lib/sem4r/bulk_mutate_job/job_operations.rb +1 -1
  48. data/lib/sem4r/campaign/campaign.rb +1 -0
  49. data/lib/sem4r/campaign/campaign_account_extension.rb +2 -1
  50. data/lib/sem4r/campaign/campaign_service.rb +9 -5
  51. data/lib/sem4r/credentials.rb +11 -5
  52. data/lib/sem4r/geo_location/address.rb +56 -0
  53. data/lib/sem4r/geo_location/geo_location_account_extension.rb +27 -6
  54. data/lib/sem4r/geo_location/geo_location_selector.rb +57 -0
  55. data/lib/sem4r/geo_location/geo_location_service.rb +9 -16
  56. data/lib/sem4r/info/info_account_extension.rb +3 -2
  57. data/lib/sem4r/info/info_selector.rb +2 -1
  58. data/lib/sem4r/info/info_service.rb +10 -3
  59. data/lib/sem4r/operation.rb +39 -20
  60. data/lib/sem4r/report_definition/report_definition.rb +37 -19
  61. data/lib/sem4r/report_definition/report_definition_account_extension.rb +66 -16
  62. data/lib/sem4r/report_definition/report_definition_operation.rb +2 -2
  63. data/lib/sem4r/report_definition/report_definition_selector.rb +1 -1
  64. data/lib/sem4r/report_definition/report_definition_service.rb +11 -7
  65. data/lib/sem4r/report_definition/report_field.rb +3 -2
  66. data/lib/sem4r/sem4r_templates.rb +77 -0
  67. data/lib/sem4r/{services/service.rb → service.rb} +23 -23
  68. data/lib/sem4r/targeting_idea/targeting_idea.rb +4 -4
  69. data/lib/sem4r/targeting_idea/targeting_idea_account_extension.rb +1 -1
  70. data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +6 -6
  71. data/lib/sem4r/targeting_idea/targeting_idea_service.rb +7 -5
  72. data/lib/sem4r/v13_account/account_account_extension.rb +7 -10
  73. data/lib/sem4r/v13_account/account_service.rb +10 -4
  74. data/lib/sem4r/v13_account/billing_address.rb +2 -2
  75. data/lib/sem4r/v13_report/report_service.rb +11 -9
  76. data/lib/sem4r.rb +46 -31
  77. data/lib/{sem4r/cli → sem4r_cli}/cli_helpers.rb +3 -2
  78. data/lib/{sem4r/cli/cli_command.rb → sem4r_cli/cli_mini_framework.rb} +77 -32
  79. data/lib/{sem4r/cli/cli_common_args.rb → sem4r_cli/cli_sem.rb} +177 -157
  80. data/lib/{sem4r/cli/commands/cli_list_ads.rb → sem4r_cli/commands/cli_ads.rb} +7 -9
  81. data/lib/sem4r_cli/commands/cli_campaign.rb +69 -0
  82. data/lib/{sem4r/cli/commands/cli_list_client.rb → sem4r_cli/commands/cli_clients.rb} +2 -2
  83. data/lib/{sem4r/cli → sem4r_cli}/commands/cli_ideas.rb +35 -27
  84. data/lib/{sem4r/cli → sem4r_cli}/commands/cli_info.rb +3 -4
  85. data/lib/{sem4r/cli/commands/cli_repdef.rb → sem4r_cli/commands/cli_job.rb} +57 -55
  86. data/lib/{sem4r/cli/commands/cli_list_keywords.rb → sem4r_cli/commands/cli_keywords.rb} +14 -13
  87. data/lib/sem4r_cli/commands/cli_profile.rb +138 -0
  88. data/lib/{sem4r/cli/commands/cli_report.rb → sem4r_cli/commands/cli_repdef.rb} +67 -20
  89. data/lib/{sem4r/cli/commands/cli_request_report.rb → sem4r_cli/commands/cli_report.rb} +82 -19
  90. data/lib/sem4r_cli.rb +5 -7
  91. data/lib/sem4r_soap/http_connector.rb +206 -0
  92. data/lib/{soap_helpers → sem4r_soap}/soap_attributes.rb +8 -3
  93. data/lib/{sem4r/services → sem4r_soap}/soap_dumper.rb +36 -20
  94. data/lib/{sem4r/services → sem4r_soap}/soap_error.rb +5 -2
  95. data/lib/sem4r_soap/soap_response.rb +75 -0
  96. data/lib/sem4r_soap/soap_service.rb +137 -0
  97. data/lib/{sem4r/cli/cli_sem.rb → sem4r_soap/soap_service_v13.rb} +27 -28
  98. data/lib/sem4r_soap/soap_service_v2010.rb +80 -0
  99. data/lib/sem4r_soap.rb +17 -0
  100. data/sem4r.gemspec +93 -58
  101. data/spec/build_fixtures.rb +49 -42
  102. data/spec/fixtures/password.example.yml +3 -0
  103. data/spec/fixtures/sem4r.example.yml +6 -0
  104. data/spec/fixtures/{services/error.xml → soap_error.xml} +0 -0
  105. data/spec/fixtures/soap_error2.xml +29 -0
  106. data/spec/helpers/dump_interceptor.rb +90 -0
  107. data/spec/helpers/fixtures_bulk_mutate_job.rb +48 -0
  108. data/spec/helpers/fixtures_geo_location.rb +45 -0
  109. data/{lib/sem4r/campaign_criterion/campaign_criterion_service.rb → spec/helpers/fixtures_info.rb} +10 -6
  110. data/spec/helpers/fixtures_report_definition.rb +65 -0
  111. data/spec/{rspec_hash.rb → helpers/rspec_hash.rb} +1 -0
  112. data/spec/{rspec_matchers.rb → helpers/rspec_matchers.rb} +46 -19
  113. data/spec/{rspec_sem4r_helper.rb → helpers/rspec_sem4r_helper.rb} +19 -16
  114. data/spec/{sem4r_stubs.rb → helpers/sem4r_stubs.rb} +5 -4
  115. data/spec/rspec_helper.rb +4 -4
  116. data/spec/sem4r/account_spec.rb +2 -3
  117. data/spec/sem4r/ad_group/ad_group_service_spec.rb +1 -1
  118. data/spec/sem4r/ad_group/ad_group_spec.rb +1 -1
  119. data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +3 -3
  120. data/spec/sem4r/adwords_spec.rb +62 -39
  121. data/{lib/sem4r/cli/commands/cli_list_campaign.rb → spec/sem4r/bulk_mutate_job/bulk_mutate_job_selector_spec.rb} +14 -11
  122. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +2 -3
  123. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +23 -17
  124. data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-req.xml +35 -0
  125. data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-res.xml +417 -0
  126. data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-req.xml +106 -0
  127. data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-res.xml +48 -0
  128. data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +5 -7
  129. data/spec/sem4r/campaign/campaign_service_spec.rb +2 -2
  130. data/spec/sem4r/credentials_spec.rb +10 -8
  131. data/spec/sem4r/geo_location/address_spec.rb +62 -0
  132. data/spec/sem4r/geo_location/fixtures/get-req.xml +42 -0
  133. data/spec/sem4r/geo_location/fixtures/get-res.xml +60 -0
  134. data/spec/sem4r/info/fixtures/get-req.xml +34 -0
  135. data/spec/sem4r/info/fixtures/get-res.xml +27 -0
  136. data/spec/sem4r/nokogiri_parsing_spec.rb +1 -0
  137. data/spec/sem4r/operation_spec.rb +72 -0
  138. data/spec/sem4r/report_definition/fixtures/get-list-repdef-req.xml +22 -0
  139. data/spec/sem4r/report_definition/fixtures/get-list-repdef-res.xml +73 -0
  140. data/spec/sem4r/report_definition/fixtures/getReportFields-req.xml +24 -0
  141. data/spec/sem4r/report_definition/fixtures/getReportFields-res.xml +1199 -0
  142. data/spec/sem4r/report_definition/fixtures/mutate-add-report-req.xml +63 -0
  143. data/spec/sem4r/report_definition/fixtures/mutate-add-report-res.xml +68 -0
  144. data/spec/sem4r/report_definition/report_definition_service_spec.rb +5 -5
  145. data/spec/sem4r/report_definition/report_definition_spec.rb +28 -43
  146. data/{lib/sem4r/cli/commands/cli_list_report.rb → spec/sem4r/report_definition/report_field_spec.rb} +12 -10
  147. data/spec/sem4r/rexml_parsing_spec.rb +1 -0
  148. data/spec/sem4r/{services/service_spec.rb → service_spec.rb} +1 -1
  149. data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +1 -1
  150. data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +1 -1
  151. data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +1 -1
  152. data/spec/sem4r/v13_account/account_service_spec.rb +2 -2
  153. data/spec/sem4r/v13_report/report_service_spec.rb +2 -2
  154. data/spec/{sem4r/cli → sem4r_cli}/cli_spec.rb +22 -27
  155. data/spec/{soap_helpers → sem4r_soap}/soap_attributes_spec.rb +3 -3
  156. data/spec/{sem4r/services/soap_message_v13_spec.rb → sem4r_soap/soap_response_spec.rb} +10 -15
  157. data/spec/{sem4r/services/soap_call_spec.rb → sem4r_soap/soap_service_spec.rb} +35 -54
  158. data/tasks/jeweler.rake +66 -0
  159. data/tasks/rspec.rake +21 -0
  160. data/tasks/sem4r.rake +25 -0
  161. data/tasks/yard.rake +31 -0
  162. metadata +173 -76
  163. data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +0 -30
  164. data/lib/sem4r/api_counters.rb +0 -8
  165. data/lib/sem4r/campaign_target/campaign_target_service.rb +0 -30
  166. data/lib/sem4r/cli/commands/cli_download_report.rb +0 -82
  167. data/lib/sem4r/services/http_connector.rb +0 -93
  168. data/lib/sem4r/services/soap_call.rb +0 -122
  169. data/lib/sem4r/services/soap_connector.rb +0 -139
  170. data/lib/sem4r/services/soap_message_v13.rb +0 -135
  171. data/lib/sem4r/services/soap_message_v2010.rb +0 -184
  172. data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +0 -30
  173. data/spec/aggregates_rspec_helper.rb +0 -59
  174. data/spec/sem4r/report_definition/fixtures/mutate_add-req.xml +0 -24
  175. data/spec/sem4r/report_definition/fixtures/mutate_add-req_orig.xml +0 -45
data/lib/sem4r_cli.rb CHANGED
@@ -23,16 +23,14 @@
23
23
  # -------------------------------------------------------------------
24
24
 
25
25
  require 'optparse'
26
- require 'ostruct'
27
- # require "highline/import" # to ask password on command line
26
+ require "highline/import" # to ask password on command line
28
27
 
29
28
  require 'sem4r'
30
- require 'sem4r/cli/cli_sem'
31
- require 'sem4r/cli/cli_common_args'
32
- require 'sem4r/cli/cli_command'
33
- require 'sem4r/cli/cli_helpers'
29
+ require 'sem4r_cli/cli_mini_framework'
30
+ require 'sem4r_cli/cli_sem'
31
+ require 'sem4r_cli/cli_helpers'
34
32
 
35
33
  # commands
36
- Dir[File.join( File.dirname(__FILE__), "sem4r", "cli", "commands", "*.rb" )].each do |f|
34
+ Dir[File.join( File.dirname(__FILE__), "sem4r_cli", "commands", "*.rb" )].each do |f|
37
35
  require f
38
36
  end
@@ -0,0 +1,206 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -------------------------------------------------------------------------
3
+ # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ # -------------------------------------------------------------------------
25
+
26
+ module Sem4rSoap
27
+
28
+ module HttpConnector
29
+
30
+ def self.get(logger = nil, type = :http_client)
31
+ case type
32
+ when :http_client
33
+ ConnectorHttpClient.new(logger)
34
+ when :net_http
35
+ ConnectorNetHttp.new(logger)
36
+ else
37
+ raise "unknow connector type"
38
+ end
39
+ end
40
+
41
+ #
42
+ # use Net::HTTP standard library http client
43
+ #
44
+ class ConnectorNetHttp
45
+ include SoapDumper
46
+
47
+ def initialize(logger = nil)
48
+ @logger = logger
49
+ end
50
+
51
+ def logger=(log)
52
+ @logger = log
53
+ end
54
+
55
+ #
56
+ # Send a soap request
57
+ #
58
+ # @raise [URI::InvalidURIError] when service_url is incorrect
59
+ #
60
+ def send(service_url, soap_action, soap_request_xml)
61
+ uri = URI.parse(service_url) # might raise URI::InvalidURIError
62
+
63
+ headers = {
64
+ "Content-Type" => "text/xml; charset=utf-8",
65
+ "Content-Length" => soap_request_xml.length.to_s,
66
+ "SOAPAction" => soap_action}
67
+
68
+ @logger.info("Post to #{uri.path} (#{soap_action})") if @logger
69
+ dump_soap_request(service_url, soap_request_xml)
70
+ response = post(uri, soap_request_xml, headers)
71
+ unless response
72
+ raise Sem4rError, "Connection Error"
73
+ end
74
+ response_xml = response.body
75
+ dump_soap_response(service_url, response_xml)
76
+ response_xml
77
+ end
78
+
79
+ #
80
+ # Downloads content at url in path_name
81
+ #
82
+ def download(url, path_name)
83
+ uri = URI.parse(url)
84
+ http = Net::HTTP.new(uri.host, uri.port)
85
+ request = Net::HTTP::Get.new(uri.request_uri)
86
+ request.initialize_http_header({"User-Agent" => "My Ruby Script"})
87
+ response = http.request(request)
88
+ File.open(path_name, "w") { |f| f.write(response.body) }
89
+ end
90
+
91
+ def post(uri, str, headers)
92
+ unless uri.respond_to? :path
93
+ uri = URI.parse(uri) # might raise URI::InvalidURIError
94
+ end
95
+ retries = 0; response = nil
96
+ while retries <= MAXRETRIES and response.nil?
97
+ retries += 1
98
+ begin
99
+ client = client_for_uri(uri)
100
+ response = client.post(uri.path, str, headers)
101
+ status = response.code.to_i
102
+ # pp response.class.to_s
103
+ # pp "status: #{status}"
104
+ rescue StandardError => e
105
+ @logger.warn("request_post retries!!! #{e.to_s}") if @logger
106
+ invalidate_client_for_uri(uri)
107
+ sleep(1 * retries) # wait
108
+ end
109
+ end
110
+
111
+ unless response
112
+ raise "Connection Error, Network is down?? :-((("
113
+ end
114
+ response
115
+ end
116
+
117
+ private
118
+
119
+ MAXRETRIES = 2
120
+
121
+ def client_for_uri(uri)
122
+ @clients ||= {}
123
+ key = uri.scheme + "://" + uri.host
124
+ client = @clients[key]
125
+ unless client
126
+ client = Net::HTTP.new(uri.host, uri.port)
127
+ if uri.scheme == "https"
128
+ client.use_ssl = (uri.scheme == "https")
129
+ client.verify_mode = OpenSSL::SSL::VERIFY_NONE
130
+ end
131
+ @clients[key] = client
132
+ end
133
+ client
134
+ end
135
+
136
+ def invalidate_client_for_uri(uri)
137
+ key = uri.scheme + "://" + uri.host
138
+ @clients[uri.host] = nil if @clients[key]
139
+ end
140
+
141
+ end
142
+
143
+ class ConnectorHttpClient
144
+ include SoapDumper
145
+
146
+ def initialize(logger = nil)
147
+ @logger = logger
148
+ end
149
+
150
+ def logger=(log)
151
+ @logger = log
152
+ end
153
+
154
+ #
155
+ # Send a soap request
156
+ # @return [String] soap xml response
157
+ # @raise [URI::InvalidURIError] when service_url is incorrect
158
+ #
159
+ def send(service_url, soap_action, request_xml)
160
+ uri = URI.parse(service_url)
161
+
162
+ headers = {
163
+ "Content-Type" => "text/xml; charset=utf-8",
164
+ "Content-Length" => request_xml.length.to_s,
165
+ "SOAPAction" => soap_action}
166
+
167
+ @logger.info("Post to #{uri.path} (#{soap_action})") if @logger
168
+ dump_soap_request(service_url, request_xml)
169
+ response = post(service_url, request_xml, headers)
170
+ unless response
171
+ raise Sem4rError, "Connection Error"
172
+ end
173
+ response_xml = response.content
174
+ dump_soap_response(service_url, response_xml)
175
+ response_xml
176
+ end
177
+
178
+ #
179
+ # Downloads content at url in path_name
180
+ #
181
+ def download(url, path_name)
182
+ client = HTTPClient.new(:agent_name => 'Ruby') # agentname
183
+ File.open(path_name, "w") do |file|
184
+ client.get_content(url) do |chunk|
185
+ file.write chunk
186
+ end
187
+ end
188
+ end
189
+
190
+ #
191
+ # @return [Object] must respond to :status and :body
192
+ #
193
+ def post(service_url, body, headers)
194
+ client = HTTPClient.new(:agent_name => 'Ruby') # agentname
195
+ response = client.post(service_url, body, headers)
196
+ unless response
197
+ raise "Connection Error, Network is down?? :-((("
198
+ end
199
+ response.instance_eval { def body; content; end}
200
+ response
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+ end # module Sem4rSoap
@@ -22,7 +22,7 @@
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  # -------------------------------------------------------------------
24
24
 
25
- module Sem4r
25
+ module Sem4rSoap
26
26
 
27
27
  class MetaSoapAttribute
28
28
  attr_reader :name, :xpath
@@ -31,7 +31,7 @@ module Sem4r
31
31
  if xpath
32
32
  @xpath = xpath
33
33
  else
34
- @xpath = name.to_s.camel_case
34
+ @xpath = name.to_s.camel_case
35
35
  end
36
36
  end
37
37
 
@@ -68,6 +68,12 @@ module Sem4r
68
68
  self
69
69
  end
70
70
 
71
+ def _to_s
72
+ self.class.attributes.map do |a|
73
+ if a.enum? then "" else self.send(a.name) end
74
+ end.join(' ')
75
+ end
76
+
71
77
  module ClassMethods
72
78
 
73
79
  def enum(set, values)
@@ -91,7 +97,6 @@ module Sem4r
91
97
  attr_reader name
92
98
  end
93
99
 
94
-
95
100
  #
96
101
  # constraints
97
102
  # :values_in
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # -------------------------------------------------------------------------
2
3
  # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
4
  #
@@ -22,27 +23,36 @@
22
23
  #
23
24
  # -------------------------------------------------------------------------
24
25
 
25
- module Sem4r
26
+ module Sem4rSoap
26
27
 
27
28
  module SoapDumper
28
29
 
29
30
  def initialize
30
- @soap_dump = false
31
+ @soap_dump = false
31
32
  @soap_dump_log = nil
32
33
  end
33
34
 
34
- def dump_soap_options( dump_options )
35
- @soap_dump = true
35
+ #
36
+ # set the options for the dumping soap message
37
+ #
38
+ # @param [Hash] options
39
+ # @option options [String] :directory
40
+ # @option options [String] :file
41
+ # @option options [String] :format
42
+ # @option options [String] :interceptor
43
+ #
44
+ def dump_soap_options(options)
45
+ @soap_dump = true
36
46
  @soap_dump_log = nil
37
47
 
38
- if dump_options[:directory]
39
- @soap_dump_dir = dump_options[:directory]
48
+ if options[:directory]
49
+ @soap_dump_dir = options[:directory]
40
50
  else
41
- @soap_dump_log = File.open( dump_options[:file], "w" )
51
+ @soap_dump_log = File.open(options[:file], "w")
42
52
  end
43
- @soap_dump_format = false || dump_options[:format]
53
+ @soap_dump_format = false || options[:format]
44
54
 
45
- @soap_dump_interceptor = dump_options[:interceptor] if dump_options[:interceptor]
55
+ @soap_dump_interceptor = options[:interceptor] if options[:interceptor]
46
56
  end
47
57
 
48
58
  def dump_soap_request(service_url, request_xml)
@@ -60,9 +70,9 @@ module Sem4r
60
70
  return unless @soap_dump
61
71
  response_xml.gsub(/<email[^>]*>.+<\/email>/, "<email>**censured**</email>")
62
72
  str = ""
63
- str << "<!-- response -->\n" unless @soap_dump_dir
73
+ str << "<!-- response -->\n" unless @soap_dump_dir
64
74
  str << xml_to_s(response_xml) << "\n"
65
- str << "<!-- end -->" unless @soap_dump_dir
75
+ str << "<!-- end -->" unless @soap_dump_dir
66
76
  dump(service_url, "res", str)
67
77
  end
68
78
 
@@ -73,12 +83,12 @@ module Sem4r
73
83
  @soap_dump_interceptor.call(service_url, type, str) if @soap_dump_interceptor
74
84
 
75
85
  if @soap_dump_dir
76
- service = service_url.match(/\/([^\/]*)$/)[1]
86
+ service = service_url.match(/\/([^\/]*)$/)[1]
77
87
  filename = Time.now.strftime "%Y%m%d-%H%M%S-%L-#{service}-#{type}.xml"
78
88
 
79
89
  FileUtils.mkdir_p(@soap_dump_dir) unless File.directory?(@soap_dump_dir)
80
90
  pathname = File.join(@soap_dump_dir, filename)
81
- File.open(pathname, "w") {|f|
91
+ File.open(pathname, "w") { |f|
82
92
  f.puts str
83
93
  }
84
94
  else
@@ -93,13 +103,19 @@ module Sem4r
93
103
  else
94
104
  # TODO: using nokogiri also for pretty print xml ?
95
105
  require 'rexml/document'
96
- xml_document = REXML::Document.new(xml)
97
- f = REXML::Formatters::Pretty.new
98
- out = String.new
99
- f.write(xml_document, out)
100
- # remove xml directive
101
- out.gsub("<?xml version='1.0' encoding='UTF-8'?>","")
106
+ begin
107
+ xml_document = REXML::Document.new(xml)
108
+ f = REXML::Formatters::Pretty.new
109
+ out = String.new
110
+ f.write(xml_document, out)
111
+ # remove xml directive
112
+ out.gsub("<?xml version='1.0' encoding='UTF-8'?>", "")
113
+ rescue
114
+ puts xml
115
+ raise
116
+ end
102
117
  end
103
118
  end
104
119
  end
105
- end
120
+
121
+ end # module Sem4rSoap
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # -------------------------------------------------------------------
2
3
  # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
4
  #
@@ -21,8 +22,9 @@
21
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23
  # -------------------------------------------------------------------
23
24
 
24
- module Sem4r
25
- class SoapError < Sem4rError
25
+ module Sem4rSoap
26
+
27
+ class SoapError # < Sem4r::Sem4rError
26
28
  # attr_reader :fault_code
27
29
  # attr_reader :fault_string
28
30
  #
@@ -35,4 +37,5 @@ module Sem4r
35
37
  # "#{@fault_code}: '#{@fault_string}'"
36
38
  # end
37
39
  end
40
+
38
41
  end
@@ -0,0 +1,75 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -------------------------------------------------------------------------
3
+ # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ # -------------------------------------------------------------------------
25
+
26
+ module Sem4rSoap
27
+ class SoapResponse
28
+
29
+ attr_reader :response
30
+ attr_reader :counters
31
+
32
+ def parse_response(response_xml)
33
+ # erase namespace so it more simple parsing the xml
34
+ response_xml.gsub!(/\b(ns\d:|xsi:|s:|soapenv:|env:|soap:)/, "")
35
+ response_xml.gsub!(/xmlns=["'].*?['"]/, '')
36
+ @response = Nokogiri::XML::Document.parse(response_xml)
37
+
38
+ #
39
+ # extract information from header
40
+ #
41
+ header = @response.at_xpath("/Envelope/Header/ResponseHeader") # v2010xx
42
+ if header
43
+ @counters = {
44
+ :operations => header.at_xpath("operations").text.to_i,
45
+ :response_time => header.at_xpath("responseTime").text.to_i,
46
+ :units => header.at_xpath("units").text.to_i
47
+ }
48
+ else
49
+ header = @response.at_xpath("/Envelope/Header") # v13
50
+ if header
51
+ @counters = {
52
+ :operations => header.at_xpath("operations").text.to_i,
53
+ :response_time => header.at_xpath("responseTime").text.to_i,
54
+ :units => header.at_xpath("units").text.to_i
55
+ }
56
+ end
57
+ end
58
+ #
59
+ # check soap fault
60
+ #
61
+ fault_el = @response.at_xpath("//Fault")
62
+ if fault_el
63
+ # fault_code = fault_el.at_xpath('faultcode').text
64
+ # fault_string = fault_el.at_xpath('faultstring').text
65
+ # raise SoapError, "#{fault_code}: '#{fault_string}'"
66
+
67
+ fault_string = fault_el.at_xpath('faultstring').text
68
+ @logger.error("soap error: #{fault_string}") if @logger
69
+ raise fault_string
70
+ end
71
+ self
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,137 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -------------------------------------------------------------------
3
+ # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ # -------------------------------------------------------------------
24
+
25
+ module Sem4rSoap
26
+
27
+ class SoapService
28
+
29
+ def initialize
30
+ @soap_namespaces = {}
31
+ end
32
+
33
+ def init(header_namespace, service_namespace)
34
+ @soap_namespaces = {}
35
+
36
+ @header_namespace = header_namespace
37
+ @soap_namespaces['xmlns'] = header_namespace if header_namespace
38
+
39
+ @service_namespace = service_namespace
40
+ @soap_namespaces['xmlns:s'] = service_namespace if service_namespace
41
+ end
42
+
43
+ def build_soap_message(soap_header, soap_body)
44
+ soap_message = '<?xml version="1.0" encoding="utf-8" ?>'
45
+ soap_message +=<<-EOFS
46
+ <env:Envelope
47
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
48
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
49
+ xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
50
+ EOFS
51
+
52
+ @soap_namespaces.each do |name, value|
53
+ soap_message += " #{name}=\"#{value}\""
54
+ end
55
+ soap_message += ">"
56
+
57
+ soap_message += soap_header
58
+ soap_message += "<env:Body>"
59
+ soap_message += soap_body
60
+ soap_message += "</env:Body>"
61
+
62
+ soap_message += "</env:Envelope>"
63
+ soap_message
64
+ end
65
+
66
+ def helper_call(api_version, credentials, soap_body)
67
+ soap_action = ""
68
+
69
+ case api_version
70
+ when "v13"
71
+ match_data = soap_body.match(/<(\w+)/m)
72
+ if match_data
73
+ soap_action = match_data[1]
74
+ else
75
+ raise "Soapaction not found in #{soap_body}"
76
+ end
77
+ when "v2010"
78
+ else
79
+ raise "unkwnow api version #{api_version}"
80
+ end
81
+
82
+ soap_xml = build_soap_message(build_soap_header(credentials), soap_body)
83
+ if credentials.sandbox?
84
+ _send(@sandbox_service_url, soap_action, soap_xml)
85
+ else
86
+ _send(@production_service_url, soap_action, soap_xml)
87
+ end
88
+ end
89
+
90
+ def helper_call_raw(credentials, xml_message)
91
+ soap_message = SoapMessageV2010.new(@connector, credentials)
92
+ soap_message.init( @header_namespace, @service_namespace )
93
+ if credentials.sandbox?
94
+ soap_message.send_raw(@sandbox_service_url, xml_message)
95
+ else
96
+ soap_message.send_raw(@production_service_url, xml_message)
97
+ end
98
+ end
99
+
100
+ def self._soap_call(api_version, method, options = {})
101
+ options.assert_valid_keys(:mutate)
102
+ mutate = options.delete :mutate
103
+ if mutate.nil? or mutate
104
+ smutate = "credentials.mutable?"
105
+ else
106
+ smutate = "true"
107
+ end
108
+ rubystr =<<-EOFS
109
+ define_method :#{method.to_sym} do |*args|
110
+ credentials = args.shift
111
+ if #{smutate}
112
+ soap_body = send("_#{method}", *args)
113
+ helper_call('#{api_version}', credentials, soap_body)
114
+ else
115
+ raise "mutate methods '#{method}' cannot be called on read_only profile"
116
+ end
117
+ end
118
+ EOFS
119
+ eval rubystr
120
+
121
+ rubystr =<<-EOFS
122
+ define_method :#{(method.to_s + "_raw").to_sym} do |*args|
123
+ credentials = args.shift
124
+ soap_message = args.shift
125
+ if #{smutate}
126
+ helper_call_raw('#{api_version}', credentials, soap_message)
127
+ else
128
+ raise "mutate methods '#{method}' cannot be called on read_only profile"
129
+ end
130
+ end
131
+ EOFS
132
+ eval rubystr
133
+ end
134
+
135
+ end
136
+
137
+ end # module Sem4rSoap
@@ -22,42 +22,41 @@
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  # -------------------------------------------------------------------
24
24
 
25
- # stdlib
25
+ module Sem4rSoap
26
26
 
27
- module Sem4r
27
+ class SoapServiceV13 < SoapService
28
28
 
29
- class CliSem
30
-
31
- def self.run
32
- cli = self.new
33
- cli.parse_and_run(ARGV)
29
+ def initialize
30
+ super
34
31
  end
35
32
 
36
- def parse_and_run( argv )
37
- common_args, command, command_args = CliCommand.split_args(argv)
38
-
39
- # $stderr.puts "debug -------------------"
40
- # $stderr.puts "common_args: #{common_args}"
41
- # $stderr.puts "command: #{command}"
42
- # $stderr.puts "command_args: #{command_args}"
43
- # $stderr.puts "debug -------------------"
33
+ def self.soap_call(method, options = {})
34
+ _soap_call("v13", method, options)
35
+ end
44
36
 
45
- cli_common_args = CliCommonArgs.new
46
- success = cli_common_args.parse_and_run(common_args)
47
- return false unless success
37
+ private
38
+
39
+ def build_soap_header(credentials)
40
+ str= <<-EOFS
41
+ <env:Header>
42
+ <email env:mustUnderstand="0">#{credentials.email}</email>
43
+ <password env:mustUnderstand="0">#{credentials.password}</password>
44
+ <useragent env:mustUnderstand="0">#{credentials.useragent}</useragent>
45
+ <developerToken env:mustUnderstand="0">#{credentials.developer_token}</developerToken>
46
+ EOFS
48
47
 
49
- if command.nil?
50
- puts "missing command try sem -h for help"
51
- return false
48
+ if credentials.client_email
49
+ str += "<clientEmail env:mustUnderstand=\"0\">#{credentials.client_email}</clientEmail>"
52
50
  end
53
51
 
54
- begin
55
- cmd = CliCommand.commands[command].new(cli_common_args)
56
- return cmd.parse_and_run(command_args)
57
- rescue Sem4rError
58
- puts "I am so sorry! Something went wrong! (exception #{$!.to_s})"
59
- return false
60
- end
52
+ str += "</env:Header>"
53
+ str
54
+ end
55
+
56
+ def _send(service_url, soap_action, soap_xml)
57
+ response_xml = @connector.send(service_url, soap_action, soap_xml)
58
+ soap_response = SoapResponse.new.parse_response(response_xml)
59
+ soap_response
61
60
  end
62
61
 
63
62
  end