stockboy 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +5 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile +12 -0
- data/Guardfile +10 -0
- data/LICENSE +21 -0
- data/README.md +293 -0
- data/Rakefile +30 -0
- data/lib/stockboy.rb +80 -0
- data/lib/stockboy/attribute.rb +11 -0
- data/lib/stockboy/attribute_map.rb +74 -0
- data/lib/stockboy/candidate_record.rb +130 -0
- data/lib/stockboy/configuration.rb +62 -0
- data/lib/stockboy/configurator.rb +176 -0
- data/lib/stockboy/dsl.rb +68 -0
- data/lib/stockboy/exceptions.rb +3 -0
- data/lib/stockboy/filter.rb +58 -0
- data/lib/stockboy/filter_chain.rb +41 -0
- data/lib/stockboy/filters.rb +11 -0
- data/lib/stockboy/filters/missing_email.rb +37 -0
- data/lib/stockboy/job.rb +241 -0
- data/lib/stockboy/mapped_record.rb +59 -0
- data/lib/stockboy/provider.rb +238 -0
- data/lib/stockboy/providers.rb +11 -0
- data/lib/stockboy/providers/file.rb +135 -0
- data/lib/stockboy/providers/ftp.rb +205 -0
- data/lib/stockboy/providers/http.rb +123 -0
- data/lib/stockboy/providers/imap.rb +290 -0
- data/lib/stockboy/providers/soap.rb +120 -0
- data/lib/stockboy/railtie.rb +28 -0
- data/lib/stockboy/reader.rb +59 -0
- data/lib/stockboy/readers.rb +11 -0
- data/lib/stockboy/readers/csv.rb +115 -0
- data/lib/stockboy/readers/fixed_width.rb +121 -0
- data/lib/stockboy/readers/spreadsheet.rb +144 -0
- data/lib/stockboy/readers/xml.rb +155 -0
- data/lib/stockboy/registry.rb +42 -0
- data/lib/stockboy/source_record.rb +43 -0
- data/lib/stockboy/string_pool.rb +35 -0
- data/lib/stockboy/template_file.rb +44 -0
- data/lib/stockboy/translations.rb +70 -0
- data/lib/stockboy/translations/boolean.rb +58 -0
- data/lib/stockboy/translations/date.rb +41 -0
- data/lib/stockboy/translations/decimal.rb +33 -0
- data/lib/stockboy/translations/default_empty_string.rb +38 -0
- data/lib/stockboy/translations/default_false.rb +41 -0
- data/lib/stockboy/translations/default_nil.rb +38 -0
- data/lib/stockboy/translations/default_true.rb +41 -0
- data/lib/stockboy/translations/default_zero.rb +41 -0
- data/lib/stockboy/translations/integer.rb +33 -0
- data/lib/stockboy/translations/string.rb +33 -0
- data/lib/stockboy/translations/time.rb +41 -0
- data/lib/stockboy/translations/uk_date.rb +51 -0
- data/lib/stockboy/translations/us_date.rb +51 -0
- data/lib/stockboy/translator.rb +66 -0
- data/lib/stockboy/version.rb +3 -0
- data/spec/fixtures/.gitkeep +0 -0
- data/spec/fixtures/files/a_garbage.csv +1 -0
- data/spec/fixtures/files/test_data-20120101.csv +1 -0
- data/spec/fixtures/files/test_data-20120202.csv +1 -0
- data/spec/fixtures/files/z_garbage.csv +1 -0
- data/spec/fixtures/jobs/test_job.rb +1 -0
- data/spec/fixtures/soap/get_list/fault.xml +8 -0
- data/spec/fixtures/soap/get_list/success.xml +18 -0
- data/spec/fixtures/spreadsheets/test_data.xls +0 -0
- data/spec/fixtures/spreadsheets/test_row_options.xls +0 -0
- data/spec/fixtures/xml/body.xml +14 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/stockboy/attribute_map_spec.rb +59 -0
- data/spec/stockboy/attribute_spec.rb +11 -0
- data/spec/stockboy/candidate_record_spec.rb +150 -0
- data/spec/stockboy/configuration_spec.rb +28 -0
- data/spec/stockboy/configurator_spec.rb +127 -0
- data/spec/stockboy/filter_chain_spec.rb +40 -0
- data/spec/stockboy/filter_spec.rb +41 -0
- data/spec/stockboy/filters/missing_email_spec.rb +26 -0
- data/spec/stockboy/filters_spec.rb +38 -0
- data/spec/stockboy/job_spec.rb +238 -0
- data/spec/stockboy/mapped_record_spec.rb +30 -0
- data/spec/stockboy/provider_spec.rb +34 -0
- data/spec/stockboy/providers/file_spec.rb +116 -0
- data/spec/stockboy/providers/ftp_spec.rb +143 -0
- data/spec/stockboy/providers/http_spec.rb +94 -0
- data/spec/stockboy/providers/imap_spec.rb +76 -0
- data/spec/stockboy/providers/soap_spec.rb +107 -0
- data/spec/stockboy/providers_spec.rb +38 -0
- data/spec/stockboy/readers/csv_spec.rb +68 -0
- data/spec/stockboy/readers/fixed_width_spec.rb +52 -0
- data/spec/stockboy/readers/spreadsheet_spec.rb +121 -0
- data/spec/stockboy/readers/xml_spec.rb +94 -0
- data/spec/stockboy/readers_spec.rb +30 -0
- data/spec/stockboy/source_record_spec.rb +19 -0
- data/spec/stockboy/template_file_spec.rb +30 -0
- data/spec/stockboy/translations/boolean_spec.rb +48 -0
- data/spec/stockboy/translations/date_spec.rb +38 -0
- data/spec/stockboy/translations/decimal_spec.rb +23 -0
- data/spec/stockboy/translations/default_empty_string_spec.rb +32 -0
- data/spec/stockboy/translations/default_false_spec.rb +25 -0
- data/spec/stockboy/translations/default_nil_spec.rb +32 -0
- data/spec/stockboy/translations/default_true_spec.rb +25 -0
- data/spec/stockboy/translations/default_zero_spec.rb +32 -0
- data/spec/stockboy/translations/integer_spec.rb +22 -0
- data/spec/stockboy/translations/string_spec.rb +22 -0
- data/spec/stockboy/translations/time_spec.rb +27 -0
- data/spec/stockboy/translations/uk_date_spec.rb +37 -0
- data/spec/stockboy/translations/us_date_spec.rb +37 -0
- data/spec/stockboy/translations_spec.rb +55 -0
- data/spec/stockboy/translator_spec.rb +27 -0
- data/stockboy.gemspec +32 -0
- metadata +305 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'stockboy/provider'
|
2
|
+
require 'httpi'
|
3
|
+
|
4
|
+
module Stockboy::Providers
|
5
|
+
|
6
|
+
# Fetches data from an HTTP endpoint
|
7
|
+
#
|
8
|
+
# == Job template DSL
|
9
|
+
#
|
10
|
+
# provider :http do
|
11
|
+
# get "http://example.com/api/things"
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class HTTP < Stockboy::Provider
|
15
|
+
|
16
|
+
# @!group Options
|
17
|
+
|
18
|
+
# Shorthand for +:method+ and +:uri+ using HTTP GET
|
19
|
+
#
|
20
|
+
# @!attribute [rw] get
|
21
|
+
# @return [String]
|
22
|
+
# @example
|
23
|
+
# get 'http://example.com/api/things'
|
24
|
+
#
|
25
|
+
dsl_attr :get, attr_writer: false
|
26
|
+
|
27
|
+
# Shorthand for +:method+ and +:uri+ using HTTP POST
|
28
|
+
#
|
29
|
+
# @!attribute [rw] post
|
30
|
+
# @return [String]
|
31
|
+
# @example
|
32
|
+
# post 'http://example.com/api/search'
|
33
|
+
#
|
34
|
+
dsl_attr :post, attr_writer: false
|
35
|
+
|
36
|
+
# HTTP method: +:get+ or +:post+
|
37
|
+
#
|
38
|
+
# @!attribute [rw] method
|
39
|
+
# @return [Symbol]
|
40
|
+
# @example
|
41
|
+
# method :post
|
42
|
+
#
|
43
|
+
dsl_attr :method, attr_writer: false
|
44
|
+
|
45
|
+
def uri
|
46
|
+
return nil if @uri.nil? || @uri.empty?
|
47
|
+
URI(@uri).tap { |u| u.query = URI.encode_www_form(@query) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def uri=(uri)
|
51
|
+
@uri = uri
|
52
|
+
end
|
53
|
+
|
54
|
+
# HTTP host and path to the data resource
|
55
|
+
#
|
56
|
+
# @!attribute [rw] uri
|
57
|
+
# @return [String]
|
58
|
+
# @example
|
59
|
+
# uri 'http://example.com/api/things'
|
60
|
+
#
|
61
|
+
dsl_attr :uri, attr_accessor: false, alias: :url
|
62
|
+
|
63
|
+
# Hash of query options
|
64
|
+
#
|
65
|
+
# @!attribute [rw] query
|
66
|
+
# @return [Hash]
|
67
|
+
# @example
|
68
|
+
# query start: 1, limit: 100
|
69
|
+
#
|
70
|
+
dsl_attr :query
|
71
|
+
|
72
|
+
def method=(http_method)
|
73
|
+
return @method = nil unless %w(get post).include? http_method.to_s.downcase
|
74
|
+
@method = http_method.to_s.downcase.to_sym
|
75
|
+
end
|
76
|
+
|
77
|
+
def get=(uri)
|
78
|
+
@method = :get
|
79
|
+
@uri = uri
|
80
|
+
end
|
81
|
+
|
82
|
+
def post=(uri)
|
83
|
+
@method = :post
|
84
|
+
@uri = uri
|
85
|
+
end
|
86
|
+
|
87
|
+
# @!endgroup
|
88
|
+
|
89
|
+
# Initialize an HTTP provider
|
90
|
+
#
|
91
|
+
def initialize(opts={}, &block)
|
92
|
+
super(opts, &block)
|
93
|
+
self.uri = opts[:uri]
|
94
|
+
self.method = opts[:method] || :get
|
95
|
+
self.query = opts[:query] || Hash.new
|
96
|
+
DSL.new(self).instance_eval(&block) if block_given?
|
97
|
+
end
|
98
|
+
|
99
|
+
def client
|
100
|
+
return HTTPI unless block_given?
|
101
|
+
yield HTTPI
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def validate
|
107
|
+
errors.add_on_blank [:uri, :method]
|
108
|
+
errors.empty?
|
109
|
+
end
|
110
|
+
|
111
|
+
def fetch_data
|
112
|
+
request = HTTPI::Request.new
|
113
|
+
request.url = uri
|
114
|
+
response = HTTPI.send(method, request)
|
115
|
+
if response.error?
|
116
|
+
errors.add :response, "HTTP respone error: #{response.code}"
|
117
|
+
else
|
118
|
+
@data = response.body
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
require 'stockboy/provider'
|
2
|
+
require 'net/imap'
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
module Stockboy::Providers
|
6
|
+
|
7
|
+
# Read data from a file attachment in IMAP email
|
8
|
+
#
|
9
|
+
# == Job template DSL
|
10
|
+
#
|
11
|
+
# provider :imap do
|
12
|
+
# host "imap.example.com"
|
13
|
+
# username "arthur@example.com"
|
14
|
+
# password "424242"
|
15
|
+
# mailbox "INBOX"
|
16
|
+
# subject "Daily Report"
|
17
|
+
# since Date.today
|
18
|
+
# file_name /report-[0-9]+\.csv/
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class IMAP < Stockboy::Provider
|
22
|
+
|
23
|
+
# Corresponds to %v mode in +DateTime#strftime+
|
24
|
+
VMS_DATE = /\A\d{2}-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d{2}\z/i
|
25
|
+
|
26
|
+
# @!group Options
|
27
|
+
|
28
|
+
# Host name or IP address for IMAP server connection
|
29
|
+
#
|
30
|
+
# @!attribute [rw] host
|
31
|
+
# @return [String]
|
32
|
+
# @example
|
33
|
+
# host "imap.example.com"
|
34
|
+
#
|
35
|
+
dsl_attr :host
|
36
|
+
|
37
|
+
# User name for connection credentials
|
38
|
+
#
|
39
|
+
# @!attribute [rw] username
|
40
|
+
# @return [String]
|
41
|
+
# @example
|
42
|
+
# username "arthur@example.com"
|
43
|
+
#
|
44
|
+
dsl_attr :username
|
45
|
+
|
46
|
+
# Password for connection credentials
|
47
|
+
#
|
48
|
+
# @!attribute [rw] password
|
49
|
+
# @return [String]
|
50
|
+
# @example
|
51
|
+
# password "424242"
|
52
|
+
#
|
53
|
+
dsl_attr :password
|
54
|
+
|
55
|
+
# Where to look for email on the server (usually "INBOX")
|
56
|
+
#
|
57
|
+
# @!attribute [rw] mailbox
|
58
|
+
# @return [String]
|
59
|
+
# @example
|
60
|
+
# mailbox "INBOX"
|
61
|
+
#
|
62
|
+
dsl_attr :mailbox
|
63
|
+
|
64
|
+
# Substring to find contained in matching email subject
|
65
|
+
#
|
66
|
+
# @!attribute [rw] subject
|
67
|
+
# @return [String]
|
68
|
+
# @example
|
69
|
+
# subject "Daily Report"
|
70
|
+
#
|
71
|
+
dsl_attr :subject
|
72
|
+
|
73
|
+
# Email address of the sender
|
74
|
+
#
|
75
|
+
# @!attribute [rw] from
|
76
|
+
# @return [String]
|
77
|
+
# @example
|
78
|
+
# from "sender+12345@example.com"
|
79
|
+
#
|
80
|
+
dsl_attr :from
|
81
|
+
|
82
|
+
# Minimum time sent for matching email
|
83
|
+
#
|
84
|
+
# @!attribute [rw] since
|
85
|
+
# @return [String]
|
86
|
+
# @example
|
87
|
+
# since Date.today
|
88
|
+
#
|
89
|
+
dsl_attr :since, alias: :newer_than
|
90
|
+
|
91
|
+
# Key-value tokens for IMAP search options
|
92
|
+
#
|
93
|
+
# @!attribute [rw] search
|
94
|
+
# @return [String]
|
95
|
+
# @example
|
96
|
+
# search ['FLAGGED', 'BODY', 'Report attached']
|
97
|
+
#
|
98
|
+
dsl_attr :search
|
99
|
+
|
100
|
+
# Name or pattern for matching attachment files. First matching attachment
|
101
|
+
# is picked, or the first attachment if not specified.
|
102
|
+
#
|
103
|
+
# @!attribute [rw] attachment
|
104
|
+
# @return [String, Regexp]
|
105
|
+
# @example
|
106
|
+
# attachment "daily-report.csv"
|
107
|
+
# attachment /daily-report-[0-9]+.csv/
|
108
|
+
#
|
109
|
+
dsl_attr :attachment
|
110
|
+
|
111
|
+
# Method for choosing which email message to process from potential
|
112
|
+
# matches. Default is last by date sent.
|
113
|
+
#
|
114
|
+
# @!attribute [rw] pick
|
115
|
+
# @return [Symbol, Proc]
|
116
|
+
# @example
|
117
|
+
# pick :last
|
118
|
+
# pick :first
|
119
|
+
# pick ->(list) {
|
120
|
+
# list.max_by { |msgid| client.fetch(msgid, 'SENTON').to_i }
|
121
|
+
# }
|
122
|
+
#
|
123
|
+
dsl_attr :pick
|
124
|
+
|
125
|
+
# @!endgroup
|
126
|
+
|
127
|
+
# Library for connection, defaults to +Net::IMAP+
|
128
|
+
#
|
129
|
+
# @!attribute [rw] imap_client
|
130
|
+
#
|
131
|
+
def self.imap_client
|
132
|
+
@imap_client ||= Net::IMAP
|
133
|
+
end
|
134
|
+
class << self
|
135
|
+
attr_writer :imap_client
|
136
|
+
end
|
137
|
+
|
138
|
+
# Initialize a new IMAP reader
|
139
|
+
#
|
140
|
+
def initialize(opts={}, &block)
|
141
|
+
super(opts, &block)
|
142
|
+
@host = opts[:host]
|
143
|
+
@username = opts[:username]
|
144
|
+
@password = opts[:password]
|
145
|
+
@mailbox = opts[:mailbox]
|
146
|
+
@subject = opts[:subject]
|
147
|
+
@from = opts[:from]
|
148
|
+
@since = opts[:since]
|
149
|
+
@search = opts[:search]
|
150
|
+
@attachment = opts[:attachment]
|
151
|
+
@file_smaller = opts[:file_smaller]
|
152
|
+
@file_larger = opts[:file_larger]
|
153
|
+
@pick = opts[:pick] || :last
|
154
|
+
DSL.new(self).instance_eval(&block) if block_given?
|
155
|
+
end
|
156
|
+
|
157
|
+
def client
|
158
|
+
raise(ArgumentError, "no block given") unless block_given?
|
159
|
+
return yield @open_client if @open_client
|
160
|
+
|
161
|
+
@open_client = ::Net::IMAP.new(host).tap do |i|
|
162
|
+
i.login(username, password)
|
163
|
+
i.examine(mailbox)
|
164
|
+
end
|
165
|
+
yield @open_client
|
166
|
+
client.disconnect
|
167
|
+
@open_client = nil
|
168
|
+
rescue ::Net::IMAP::Error => e
|
169
|
+
errors.add :response, "IMAP connection error"
|
170
|
+
client.disconnect
|
171
|
+
@open_client = nil
|
172
|
+
end
|
173
|
+
|
174
|
+
def delete_data
|
175
|
+
raise Stockboy::OutOfSequence, "must confirm #matching_message or calling #data" unless picked_matching_message?
|
176
|
+
|
177
|
+
logger.info "Deleting message #{username}:#{host} message_uid: #{matching_message}"
|
178
|
+
client do |imap|
|
179
|
+
imap.uid_store(matching_message, "+FLAGS", [:Deleted])
|
180
|
+
imap.expunge
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def matching_message
|
185
|
+
return @matching_message if @matching_message
|
186
|
+
keys = fetch_imap_message_keys
|
187
|
+
@matching_message = pick_from(keys) unless keys.empty?
|
188
|
+
end
|
189
|
+
|
190
|
+
def clear
|
191
|
+
super
|
192
|
+
@matching_message = nil
|
193
|
+
@data_time = nil
|
194
|
+
@data_size = nil
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def fetch_data
|
200
|
+
client do |imap|
|
201
|
+
return false unless matching_message
|
202
|
+
mail = ::Mail.new(imap.fetch(matching_message, 'RFC822')[0].attr['RFC822'])
|
203
|
+
if part = mail.attachments.detect { |part| validate_attachment(part) }
|
204
|
+
validate_file(part.decoded)
|
205
|
+
if valid?
|
206
|
+
logger.info "Getting file from #{username}:#{host} message_uid #{matching_message}"
|
207
|
+
@data = part.decoded
|
208
|
+
@data_time = normalize_imap_datetime(mail.date)
|
209
|
+
logger.info "Got file from #{username}:#{host} message_uid #{matching_message}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
!@data.nil?
|
214
|
+
end
|
215
|
+
|
216
|
+
def validate
|
217
|
+
errors.add_on_blank [:host, :username, :password]
|
218
|
+
errors.empty?
|
219
|
+
end
|
220
|
+
|
221
|
+
def fetch_imap_message_keys
|
222
|
+
client { |imap| imap.sort(['DATE'], search_keys, 'UTF-8') }
|
223
|
+
end
|
224
|
+
|
225
|
+
def picked_matching_message?
|
226
|
+
!!@matching_message
|
227
|
+
end
|
228
|
+
|
229
|
+
def validate_attachment(part)
|
230
|
+
case attachment
|
231
|
+
when String
|
232
|
+
part.filename == attachment
|
233
|
+
when Regexp
|
234
|
+
part.filename =~ attachment
|
235
|
+
else
|
236
|
+
true
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def search_keys
|
241
|
+
keys = []
|
242
|
+
keys.concat ['SUBJECT', subject] if subject
|
243
|
+
keys.concat ['FROM', from] if from
|
244
|
+
keys.concat ['SINCE', date_format(since)] if since
|
245
|
+
keys.concat search if search
|
246
|
+
keys
|
247
|
+
end
|
248
|
+
|
249
|
+
def date_format(value)
|
250
|
+
case value
|
251
|
+
when Date, Time, DateTime
|
252
|
+
value.strftime('%v')
|
253
|
+
when Numeric
|
254
|
+
Time.at(value).strftime('%v')
|
255
|
+
when String
|
256
|
+
return value if value =~ VMS_DATE
|
257
|
+
Date.parse(value).strftime('%v')
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# If activesupport is loaded, it mucks with DateTime#to_time to return
|
262
|
+
# self when it has a utc_offset. Handle both to always return a Time.utc.
|
263
|
+
#
|
264
|
+
def normalize_imap_datetime(datetime)
|
265
|
+
datetime.respond_to?(:getutc) ?
|
266
|
+
datetime.getutc.to_time : datetime.to_time.utc
|
267
|
+
end
|
268
|
+
|
269
|
+
def validate_file(data_file)
|
270
|
+
return errors.add :response, "No matching attachments" unless data_file
|
271
|
+
validate_file_smaller(data_file)
|
272
|
+
validate_file_larger(data_file)
|
273
|
+
end
|
274
|
+
|
275
|
+
def validate_file_smaller(data_file)
|
276
|
+
@data_size ||= data_file.bytesize
|
277
|
+
if file_smaller && @data_size > file_smaller
|
278
|
+
errors.add :response, "File size larger than #{file_smaller}"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def validate_file_larger(data_file)
|
283
|
+
@data_size ||= data_file.bytesize
|
284
|
+
if file_larger && @data_size < file_larger
|
285
|
+
errors.add :response, "File size smaller than #{file_larger}"
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'stockboy/provider'
|
2
|
+
require 'stockboy/string_pool'
|
3
|
+
require 'savon'
|
4
|
+
|
5
|
+
module Stockboy::Providers
|
6
|
+
|
7
|
+
# Fetch data from a SOAP endpoint
|
8
|
+
#
|
9
|
+
# Backed by Savon gem, see savon for full configuration options: extra
|
10
|
+
# options are passed through.
|
11
|
+
#
|
12
|
+
class SOAP < Stockboy::Provider
|
13
|
+
include Stockboy::StringPool
|
14
|
+
|
15
|
+
# @!group Options
|
16
|
+
#
|
17
|
+
# These options correspond to Savon client options
|
18
|
+
|
19
|
+
# URL with the WSDL document
|
20
|
+
#
|
21
|
+
# @!attribute [rw] wsdl
|
22
|
+
# @return [String]
|
23
|
+
# @example
|
24
|
+
# wsdl "http://example.com/api/soap?wsdl"
|
25
|
+
#
|
26
|
+
dsl_attr :wsdl
|
27
|
+
|
28
|
+
# The name of the request, see your SOAP documentation
|
29
|
+
#
|
30
|
+
# @!attribute [rw] request
|
31
|
+
# @return [String]
|
32
|
+
# @example
|
33
|
+
# request "allItemsDetails"
|
34
|
+
#
|
35
|
+
dsl_attr :request
|
36
|
+
|
37
|
+
# @return [String]
|
38
|
+
# @!attribute [rw] namespace
|
39
|
+
# Optional if specified in WSDL
|
40
|
+
#
|
41
|
+
dsl_attr :namespace
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
# @!attribute [rw] namespace_id
|
45
|
+
# Optional if specified in WSDL
|
46
|
+
#
|
47
|
+
dsl_attr :namespace_id
|
48
|
+
|
49
|
+
# @return [String]
|
50
|
+
# @!attribute [rw] endpoint
|
51
|
+
# Optional if specified in WSDL
|
52
|
+
#
|
53
|
+
dsl_attr :endpoint
|
54
|
+
|
55
|
+
# Hash of message options passed in the request, often includes
|
56
|
+
# credentials and query options.
|
57
|
+
#
|
58
|
+
# @!attribute [rw] message
|
59
|
+
# @return [Hash]
|
60
|
+
# @example
|
61
|
+
# message "clientId" => "12345", "updatedSince" => "2012-12-12"
|
62
|
+
#
|
63
|
+
dsl_attr :message
|
64
|
+
|
65
|
+
# Hash of optional HTTP request headers
|
66
|
+
#
|
67
|
+
# @!attribute [rw] headers
|
68
|
+
# @return [Hash]
|
69
|
+
# @example
|
70
|
+
# headers "X-ClientKey" => "12345"
|
71
|
+
#
|
72
|
+
dsl_attr :headers
|
73
|
+
|
74
|
+
# @!endgroup
|
75
|
+
|
76
|
+
# Initialize a new SOAP provider
|
77
|
+
#
|
78
|
+
def initialize(opts={}, &block)
|
79
|
+
super
|
80
|
+
DSL.new(self).instance_eval(&block) if block_given?
|
81
|
+
end
|
82
|
+
|
83
|
+
# Connection object to the configured SOAP endpoint
|
84
|
+
#
|
85
|
+
# @return [Savon::Client]
|
86
|
+
#
|
87
|
+
def client
|
88
|
+
@client ||= Savon.client(client_options)
|
89
|
+
return @client unless block_given?
|
90
|
+
yield @client
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def client_options
|
96
|
+
opts = if wsdl
|
97
|
+
{wsdl: wsdl}
|
98
|
+
elsif endpoint
|
99
|
+
{endpoint: endpoint}
|
100
|
+
end
|
101
|
+
opts[:convert_response_tags_to] = ->(tag) { string_pool(tag) }
|
102
|
+
opts[:namespace] = namespace if namespace
|
103
|
+
opts[:namespace_identifier] = namespace_id if namespace_id
|
104
|
+
opts[:headers] = headers if headers
|
105
|
+
opts
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate
|
109
|
+
errors.add_on_blank(:endpoint) unless wsdl
|
110
|
+
errors.blank?
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch_data
|
114
|
+
with_string_pool do
|
115
|
+
@data = client.call(@request, message: message).body
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|