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.
- data/Gemfile +13 -2
- data/Gemfile.lock +13 -7
- data/README.rdoc +44 -7
- data/Rakefile +2 -122
- data/VERSION.yml +1 -1
- data/bin/sem +1 -0
- data/examples_sem4r/01_get_account.rb +1 -0
- data/examples_sem4r/02_get_info.rb +1 -0
- data/examples_sem4r/03_list_ad.rb +1 -0
- data/examples_sem4r/04_list_keywords.rb +1 -0
- data/examples_sem4r/05_request_report.rb +25 -14
- data/examples_sem4r/{05_request_report_2010.rb → 06_request_report_definition.rb} +2 -4
- data/examples_sem4r/{06_create_campaigns.rb → 07_create_campaigns.rb} +1 -0
- data/examples_sem4r/{07_create_campaigns_block.rb → 08_create_campaigns_block.rb} +1 -0
- data/examples_sem4r/{07_create_campaigns_simple.rb → 09_create_campaigns_simple.rb} +1 -0
- data/examples_sem4r/{08_ad_params.rb → 10_ad_params.rb} +1 -0
- data/examples_sem4r/{09_targeting_idea.rb → 11_targeting_idea.rb} +1 -0
- data/examples_sem4r/{10_get_location.rb → 12_get_location.rb} +1 -0
- data/examples_sem4r/{11_submit_bulk_job.rb → 13_submit_bulk_job.rb} +1 -1
- data/examples_sem4r/{12_list_bulk_job.rb → 14_list_bulk_job.rb} +3 -0
- data/examples_sem4r/30_prune_empty_adgroup.rb +1 -0
- data/examples_sem4r/31_empty_accounts.rb +1 -0
- data/examples_sem4r/example_helper.rb +2 -1
- data/lib/sem4r/ad_group/ad_group.rb +22 -7
- data/lib/sem4r/ad_group/ad_group_bids.rb +1 -1
- data/lib/sem4r/ad_group/ad_group_service.rb +12 -5
- data/lib/sem4r/ad_group_ad/ad_group_ad.rb +1 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +1 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +14 -9
- data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +16 -25
- data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +18 -14
- data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +27 -19
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +2 -1
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +1 -0
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +9 -4
- data/lib/sem4r/ad_group_criterion/criterion.rb +1 -0
- data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +1 -0
- data/lib/sem4r/ad_group_criterion/criterion_placement.rb +1 -0
- data/lib/sem4r/ad_param/ad_param.rb +24 -8
- data/lib/sem4r/ad_param/ad_param_service.rb +8 -5
- data/lib/sem4r/adwords.rb +232 -76
- data/lib/sem4r/base.rb +1 -1
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +41 -35
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_account_extension.rb +49 -15
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +29 -20
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +13 -14
- data/lib/sem4r/bulk_mutate_job/job_operations.rb +1 -1
- data/lib/sem4r/campaign/campaign.rb +1 -0
- data/lib/sem4r/campaign/campaign_account_extension.rb +2 -1
- data/lib/sem4r/campaign/campaign_service.rb +9 -5
- data/lib/sem4r/credentials.rb +11 -5
- data/lib/sem4r/geo_location/address.rb +56 -0
- data/lib/sem4r/geo_location/geo_location_account_extension.rb +27 -6
- data/lib/sem4r/geo_location/geo_location_selector.rb +57 -0
- data/lib/sem4r/geo_location/geo_location_service.rb +9 -16
- data/lib/sem4r/info/info_account_extension.rb +3 -2
- data/lib/sem4r/info/info_selector.rb +2 -1
- data/lib/sem4r/info/info_service.rb +10 -3
- data/lib/sem4r/operation.rb +39 -20
- data/lib/sem4r/report_definition/report_definition.rb +37 -19
- data/lib/sem4r/report_definition/report_definition_account_extension.rb +66 -16
- data/lib/sem4r/report_definition/report_definition_operation.rb +2 -2
- data/lib/sem4r/report_definition/report_definition_selector.rb +1 -1
- data/lib/sem4r/report_definition/report_definition_service.rb +11 -7
- data/lib/sem4r/report_definition/report_field.rb +3 -2
- data/lib/sem4r/sem4r_templates.rb +77 -0
- data/lib/sem4r/{services/service.rb → service.rb} +23 -23
- data/lib/sem4r/targeting_idea/targeting_idea.rb +4 -4
- data/lib/sem4r/targeting_idea/targeting_idea_account_extension.rb +1 -1
- data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +6 -6
- data/lib/sem4r/targeting_idea/targeting_idea_service.rb +7 -5
- data/lib/sem4r/v13_account/account_account_extension.rb +7 -10
- data/lib/sem4r/v13_account/account_service.rb +10 -4
- data/lib/sem4r/v13_account/billing_address.rb +2 -2
- data/lib/sem4r/v13_report/report_service.rb +11 -9
- data/lib/sem4r.rb +46 -31
- data/lib/{sem4r/cli → sem4r_cli}/cli_helpers.rb +3 -2
- data/lib/{sem4r/cli/cli_command.rb → sem4r_cli/cli_mini_framework.rb} +77 -32
- data/lib/{sem4r/cli/cli_common_args.rb → sem4r_cli/cli_sem.rb} +177 -157
- data/lib/{sem4r/cli/commands/cli_list_ads.rb → sem4r_cli/commands/cli_ads.rb} +7 -9
- data/lib/sem4r_cli/commands/cli_campaign.rb +69 -0
- data/lib/{sem4r/cli/commands/cli_list_client.rb → sem4r_cli/commands/cli_clients.rb} +2 -2
- data/lib/{sem4r/cli → sem4r_cli}/commands/cli_ideas.rb +35 -27
- data/lib/{sem4r/cli → sem4r_cli}/commands/cli_info.rb +3 -4
- data/lib/{sem4r/cli/commands/cli_repdef.rb → sem4r_cli/commands/cli_job.rb} +57 -55
- data/lib/{sem4r/cli/commands/cli_list_keywords.rb → sem4r_cli/commands/cli_keywords.rb} +14 -13
- data/lib/sem4r_cli/commands/cli_profile.rb +138 -0
- data/lib/{sem4r/cli/commands/cli_report.rb → sem4r_cli/commands/cli_repdef.rb} +67 -20
- data/lib/{sem4r/cli/commands/cli_request_report.rb → sem4r_cli/commands/cli_report.rb} +82 -19
- data/lib/sem4r_cli.rb +5 -7
- data/lib/sem4r_soap/http_connector.rb +206 -0
- data/lib/{soap_helpers → sem4r_soap}/soap_attributes.rb +8 -3
- data/lib/{sem4r/services → sem4r_soap}/soap_dumper.rb +36 -20
- data/lib/{sem4r/services → sem4r_soap}/soap_error.rb +5 -2
- data/lib/sem4r_soap/soap_response.rb +75 -0
- data/lib/sem4r_soap/soap_service.rb +137 -0
- data/lib/{sem4r/cli/cli_sem.rb → sem4r_soap/soap_service_v13.rb} +27 -28
- data/lib/sem4r_soap/soap_service_v2010.rb +80 -0
- data/lib/sem4r_soap.rb +17 -0
- data/sem4r.gemspec +93 -58
- data/spec/build_fixtures.rb +49 -42
- data/spec/fixtures/password.example.yml +3 -0
- data/spec/fixtures/sem4r.example.yml +6 -0
- data/spec/fixtures/{services/error.xml → soap_error.xml} +0 -0
- data/spec/fixtures/soap_error2.xml +29 -0
- data/spec/helpers/dump_interceptor.rb +90 -0
- data/spec/helpers/fixtures_bulk_mutate_job.rb +48 -0
- data/spec/helpers/fixtures_geo_location.rb +45 -0
- data/{lib/sem4r/campaign_criterion/campaign_criterion_service.rb → spec/helpers/fixtures_info.rb} +10 -6
- data/spec/helpers/fixtures_report_definition.rb +65 -0
- data/spec/{rspec_hash.rb → helpers/rspec_hash.rb} +1 -0
- data/spec/{rspec_matchers.rb → helpers/rspec_matchers.rb} +46 -19
- data/spec/{rspec_sem4r_helper.rb → helpers/rspec_sem4r_helper.rb} +19 -16
- data/spec/{sem4r_stubs.rb → helpers/sem4r_stubs.rb} +5 -4
- data/spec/rspec_helper.rb +4 -4
- data/spec/sem4r/account_spec.rb +2 -3
- data/spec/sem4r/ad_group/ad_group_service_spec.rb +1 -1
- data/spec/sem4r/ad_group/ad_group_spec.rb +1 -1
- data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +3 -3
- data/spec/sem4r/adwords_spec.rb +62 -39
- data/{lib/sem4r/cli/commands/cli_list_campaign.rb → spec/sem4r/bulk_mutate_job/bulk_mutate_job_selector_spec.rb} +14 -11
- data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +2 -3
- data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +23 -17
- data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-req.xml +35 -0
- data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-res.xml +417 -0
- data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-req.xml +106 -0
- data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-res.xml +48 -0
- data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +5 -7
- data/spec/sem4r/campaign/campaign_service_spec.rb +2 -2
- data/spec/sem4r/credentials_spec.rb +10 -8
- data/spec/sem4r/geo_location/address_spec.rb +62 -0
- data/spec/sem4r/geo_location/fixtures/get-req.xml +42 -0
- data/spec/sem4r/geo_location/fixtures/get-res.xml +60 -0
- data/spec/sem4r/info/fixtures/get-req.xml +34 -0
- data/spec/sem4r/info/fixtures/get-res.xml +27 -0
- data/spec/sem4r/nokogiri_parsing_spec.rb +1 -0
- data/spec/sem4r/operation_spec.rb +72 -0
- data/spec/sem4r/report_definition/fixtures/get-list-repdef-req.xml +22 -0
- data/spec/sem4r/report_definition/fixtures/get-list-repdef-res.xml +73 -0
- data/spec/sem4r/report_definition/fixtures/getReportFields-req.xml +24 -0
- data/spec/sem4r/report_definition/fixtures/getReportFields-res.xml +1199 -0
- data/spec/sem4r/report_definition/fixtures/mutate-add-report-req.xml +63 -0
- data/spec/sem4r/report_definition/fixtures/mutate-add-report-res.xml +68 -0
- data/spec/sem4r/report_definition/report_definition_service_spec.rb +5 -5
- data/spec/sem4r/report_definition/report_definition_spec.rb +28 -43
- data/{lib/sem4r/cli/commands/cli_list_report.rb → spec/sem4r/report_definition/report_field_spec.rb} +12 -10
- data/spec/sem4r/rexml_parsing_spec.rb +1 -0
- data/spec/sem4r/{services/service_spec.rb → service_spec.rb} +1 -1
- data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +1 -1
- data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +1 -1
- data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +1 -1
- data/spec/sem4r/v13_account/account_service_spec.rb +2 -2
- data/spec/sem4r/v13_report/report_service_spec.rb +2 -2
- data/spec/{sem4r/cli → sem4r_cli}/cli_spec.rb +22 -27
- data/spec/{soap_helpers → sem4r_soap}/soap_attributes_spec.rb +3 -3
- data/spec/{sem4r/services/soap_message_v13_spec.rb → sem4r_soap/soap_response_spec.rb} +10 -15
- data/spec/{sem4r/services/soap_call_spec.rb → sem4r_soap/soap_service_spec.rb} +35 -54
- data/tasks/jeweler.rake +66 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/sem4r.rake +25 -0
- data/tasks/yard.rake +31 -0
- metadata +173 -76
- data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +0 -30
- data/lib/sem4r/api_counters.rb +0 -8
- data/lib/sem4r/campaign_target/campaign_target_service.rb +0 -30
- data/lib/sem4r/cli/commands/cli_download_report.rb +0 -82
- data/lib/sem4r/services/http_connector.rb +0 -93
- data/lib/sem4r/services/soap_call.rb +0 -122
- data/lib/sem4r/services/soap_connector.rb +0 -139
- data/lib/sem4r/services/soap_message_v13.rb +0 -135
- data/lib/sem4r/services/soap_message_v2010.rb +0 -184
- data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +0 -30
- data/spec/aggregates_rspec_helper.rb +0 -59
- data/spec/sem4r/report_definition/fixtures/mutate_add-req.xml +0 -24
- 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
|
|
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 '
|
|
31
|
-
require '
|
|
32
|
-
require '
|
|
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__), "
|
|
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
|
|
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
|
|
26
|
+
module Sem4rSoap
|
|
26
27
|
|
|
27
28
|
module SoapDumper
|
|
28
29
|
|
|
29
30
|
def initialize
|
|
30
|
-
@soap_dump
|
|
31
|
+
@soap_dump = false
|
|
31
32
|
@soap_dump_log = nil
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
39
|
-
@soap_dump_dir =
|
|
48
|
+
if options[:directory]
|
|
49
|
+
@soap_dump_dir = options[:directory]
|
|
40
50
|
else
|
|
41
|
-
@soap_dump_log = File.open(
|
|
51
|
+
@soap_dump_log = File.open(options[:file], "w")
|
|
42
52
|
end
|
|
43
|
-
@soap_dump_format = false ||
|
|
53
|
+
@soap_dump_format = false || options[:format]
|
|
44
54
|
|
|
45
|
-
@soap_dump_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 <<
|
|
73
|
+
str << "<!-- response -->\n" unless @soap_dump_dir
|
|
64
74
|
str << xml_to_s(response_xml) << "\n"
|
|
65
|
-
str <<
|
|
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
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
|
25
|
-
|
|
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
|
-
|
|
25
|
+
module Sem4rSoap
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
class SoapServiceV13 < SoapService
|
|
28
28
|
|
|
29
|
-
|
|
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
|
|
37
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
return false
|
|
48
|
+
if credentials.client_email
|
|
49
|
+
str += "<clientEmail env:mustUnderstand=\"0\">#{credentials.client_email}</clientEmail>"
|
|
52
50
|
end
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|