qbwc 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -1
  3. data/.travis.yml +6 -0
  4. data/README.md +152 -144
  5. data/Rakefile +7 -1
  6. data/lib/generators/qbwc/install/install_generator.rb +20 -4
  7. data/lib/generators/qbwc/install/templates/config/qbwc.rb +42 -26
  8. data/lib/generators/qbwc/install/templates/controllers/qbwc_controller.rb +2 -38
  9. data/lib/generators/qbwc/install/templates/db/migrate/create_qbwc_jobs.rb +15 -0
  10. data/lib/generators/qbwc/install/templates/db/migrate/create_qbwc_sessions.rb +16 -0
  11. data/lib/qbwc.rb +107 -71
  12. data/lib/qbwc/active_record.rb +6 -0
  13. data/lib/qbwc/active_record/job.rb +111 -0
  14. data/lib/qbwc/active_record/session.rb +52 -0
  15. data/lib/qbwc/controller.rb +176 -0
  16. data/lib/qbwc/job.rb +81 -18
  17. data/lib/qbwc/railtie.rb +8 -0
  18. data/lib/qbwc/request.rb +14 -23
  19. data/lib/qbwc/session.rb +100 -72
  20. data/lib/qbwc/version.rb +1 -1
  21. data/lib/qbwc/worker.rb +16 -0
  22. data/qbwc.gemspec +11 -5
  23. data/test/qbwc/controllers/controller_test.rb +157 -0
  24. data/test/qbwc/integration/job_management_test.rb +86 -0
  25. data/test/qbwc/integration/request_generation_test.rb +340 -0
  26. data/test/qbwc/integration/response_test.rb +303 -0
  27. data/test/qbwc/integration/routes_test.rb +38 -0
  28. data/test/qbwc/integration/session_test.rb +94 -0
  29. data/test/test_helper.rb +248 -0
  30. data/test/wash_out_helper.rb +76 -0
  31. metadata +133 -55
  32. data/Gemfile.lock +0 -72
  33. data/lib/qbwc/soap_wrapper.rb +0 -35
  34. data/lib/qbwc/soap_wrapper/QBWebConnectorSvc.rb +0 -69
  35. data/lib/qbwc/soap_wrapper/QBWebConnectorSvc.wsdl +0 -312
  36. data/lib/qbwc/soap_wrapper/default.rb +0 -198
  37. data/lib/qbwc/soap_wrapper/defaultMappingRegistry.rb +0 -163
  38. data/lib/qbwc/soap_wrapper/defaultServant.rb +0 -133
  39. data/spec/spec_helper.rb +0 -17
@@ -0,0 +1,176 @@
1
+ require 'wash_out/version'
2
+ include WashOut
3
+
4
+ module QBWC
5
+ module Controller
6
+
7
+ AUTHENTICATE_NOT_VALID_USER = 'nvu'
8
+ AUTHENTICATE_NO_WORK = 'none'
9
+
10
+ def self.included(base)
11
+ base.class_eval do
12
+ include WashOut::SOAP
13
+ skip_before_filter :_parse_soap_parameters, :_authenticate_wsse, :_map_soap_parameters, :only => :qwc
14
+ before_filter :get_session, :except => [:qwc, :authenticate, :_generate_wsdl]
15
+ after_filter :save_session, :except => [:qwc, :authenticate, :_generate_wsdl, :close_connection, :connection_error]
16
+
17
+ # wash_out changed the format of app/views/wash_with_soap/rpc/response.builder in commit
18
+ # https://github.com/inossidabile/wash_out/commit/24a77f4a3d874562732c6e8c3a30e8defafea7cb
19
+ wash_out_xml_namespace = (Gem::Version.new(WashOut::VERSION) < Gem::Version.new('0.9.1') ? 'tns:' : '')
20
+
21
+ soap_action 'serverVersion', :to => :server_version,
22
+ :return => {'tns:serverVersionResult' => :string},
23
+ :response_tag => "#{wash_out_xml_namespace}serverVersionResponse"
24
+
25
+ soap_action 'clientVersion', :to => :client_version,
26
+ :args => {:strVersion => :string},
27
+ :return => {'tns:clientVersionResult' => :string},
28
+ :response_tag => "#{wash_out_xml_namespace}clientVersionResponse"
29
+
30
+ soap_action 'authenticate',
31
+ :args => {:strUserName => :string, :strPassword => :string},
32
+ :return => {'tns:authenticateResult' => StringArray},
33
+ :response_tag => "#{wash_out_xml_namespace}authenticateResponse"
34
+
35
+ soap_action 'sendRequestXML', :to => :send_request,
36
+ :args => {:ticket => :string, :strHCPResponse => :string, :strCompanyFilename => :string, :qbXMLCountry => :string, :qbXMLMajorVers => :string, :qbXMLMinorVers => :string},
37
+ :return => {'tns:sendRequestXMLResult' => :string},
38
+ :response_tag => "#{wash_out_xml_namespace}sendRequestXMLResponse"
39
+
40
+ soap_action 'receiveResponseXML', :to => :receive_response,
41
+ :args => {:ticket => :string, :response => :string, :hresult => :string, :message => :string},
42
+ :return => {'tns:receiveResponseXMLResult' => :integer},
43
+ :response_tag => "#{wash_out_xml_namespace}receiveResponseXMLResponse"
44
+
45
+ soap_action 'closeConnection', :to => :close_connection,
46
+ :args => {:ticket => :string},
47
+ :return => {'tns:closeConnectionResult' => :string},
48
+ :response_tag => "#{wash_out_xml_namespace}closeConnectionResponse"
49
+
50
+ soap_action 'connectionError', :to => :connection_error,
51
+ :args => {:ticket => :string, :hresult => :string, :message => :string},
52
+ :return => {'tns:connectionErrorResult' => :string},
53
+ :response_tag => "#{wash_out_xml_namespace}connectionErrorResponse"
54
+
55
+ soap_action 'getLastError', :to => :get_last_error,
56
+ :args => {:ticket => :string},
57
+ :return => {'tns:getLastErrorResult' => :string},
58
+ :response_tag => "#{wash_out_xml_namespace}getLastErrorResponse"
59
+ end
60
+ end
61
+
62
+ def qwc
63
+ # Optional tag
64
+ scheduler_block = ''
65
+ if !QBWC.minutes_to_run.nil?
66
+ scheduler_block = <<SB
67
+ <Scheduler>
68
+ <RunEveryNMinutes>#{QBWC.minutes_to_run}</RunEveryNMinutes>
69
+ </Scheduler>
70
+ SB
71
+ end
72
+
73
+ qwc = <<QWC
74
+ <QBWCXML>
75
+ <AppName>#{Rails.application.class.parent_name} #{Rails.env} #{@app_name_suffix}</AppName>
76
+ <AppID></AppID>
77
+ <AppURL>#{qbwc_action_url(:only_path => false)}</AppURL>
78
+ <AppDescription>Quickbooks integration</AppDescription>
79
+ <AppSupport>#{QBWC.support_site_url || root_url(:protocol => 'https://')}</AppSupport>
80
+ <UserName>#{@username || QBWC.username}</UserName>
81
+ <OwnerID>#{QBWC.owner_id}</OwnerID>
82
+ <FileID>{90A44FB5-33D9-4815-AC85-BC87A7E7D1EB}</FileID>
83
+ <QBType>QBFS</QBType>
84
+ <Style>Document</Style>
85
+ #{scheduler_block}
86
+ </QBWCXML>
87
+ QWC
88
+ send_data qwc, :filename => "#{@filename || Rails.application.class.parent_name}.qwc", :content_type => 'application/x-qwc'
89
+ end
90
+
91
+ class StringArray < WashOut::Type
92
+ map "tns:string" => [:string]
93
+ end
94
+
95
+ def server_version
96
+ render :soap => {"tns:serverVersionResult" => server_version_response}
97
+ end
98
+
99
+ def client_version
100
+ render :soap => {"tns:clientVersionResult" => check_client_version}
101
+ end
102
+
103
+ def authenticate
104
+ username = params[:strUserName]
105
+ password = params[:strPassword]
106
+ if !QBWC.authenticator.nil?
107
+ company_file_path = QBWC.authenticator.call(username, password)
108
+ elsif username == QBWC.username && password == QBWC.password
109
+ company_file_path = QBWC.company_file_path
110
+ else
111
+ company_file_path = nil
112
+ end
113
+
114
+ ticket = nil
115
+ if company_file_path.nil?
116
+ QBWC.logger.info "Authentication of user '#{username}' failed."
117
+ company_file_path = AUTHENTICATE_NOT_VALID_USER
118
+ elsif !QBWC.pending_jobs(company_file_path).present?
119
+ QBWC.logger.info "Authentication of user '#{username}' succeeded, but no jobs pending for '#{company_file_path}'."
120
+ company_file_path = AUTHENTICATE_NO_WORK
121
+ else
122
+ QBWC.logger.info "Authentication of user '#{username}' succeeded, jobs are pending for '#{company_file_path}'."
123
+ ticket = QBWC.storage_module::Session.new(username, company_file_path).ticket
124
+ QBWC.session_initializer.call(get_session(ticket)) unless QBWC.session_initializer.nil?
125
+ end
126
+ render :soap => {"tns:authenticateResult" => {"tns:string" => [ticket || '', company_file_path]}}
127
+ end
128
+
129
+ def send_request
130
+ request = @session.request_to_send
131
+ render :soap => {'tns:sendRequestXMLResult' => request}
132
+ end
133
+
134
+ def receive_response
135
+ if params[:hresult]
136
+ QBWC.logger.warn "#{params[:hresult]}: #{params[:message]}"
137
+ @session.error = params[:message]
138
+ @session.status_code = params[:hresult]
139
+ @session.status_severity = 'Error'
140
+ end
141
+ @session.response = params[:response]
142
+ render :soap => {'tns:receiveResponseXMLResult' => (QBWC::on_error == 'continueOnError' || @session.error.nil?) ? @session.progress : -1}
143
+ end
144
+
145
+ def close_connection
146
+ @session.destroy
147
+ render :soap => {'tns:closeConnectionResult' => 'OK'}
148
+ end
149
+
150
+ def connection_error
151
+ @session.destroy
152
+ logger.warn "#{params[:hresult]}: #{params[:message]}"
153
+ render :soap => {'tns:connectionErrorResult' => 'done'}
154
+ end
155
+
156
+ def get_last_error
157
+ render :soap => {'tns:getLastErrorResult' => @session.error || ''}
158
+ end
159
+
160
+ protected
161
+
162
+ def get_session(ticket = params[:ticket])
163
+ @session = QBWC.storage_module::Session.get(ticket)
164
+ end
165
+
166
+ def save_session
167
+ @session.save if @session
168
+ end
169
+
170
+ def server_version_response
171
+ end
172
+
173
+ def check_client_version
174
+ end
175
+ end
176
+ end
@@ -1,47 +1,110 @@
1
1
  class QBWC::Job
2
2
 
3
- attr_reader :name, :response_proc, :requests
3
+ attr_reader :name, :company, :worker_class
4
4
 
5
- def initialize(name, &block)
5
+ def initialize(name, enabled, company, worker_class, requests = [], data = nil)
6
6
  @name = name
7
- @enabled = true
8
- @requests = block
7
+ @enabled = enabled
8
+ @company = company || QBWC.company_file_path
9
+ @worker_class = worker_class
10
+ @requests = requests
11
+ @data = data
12
+ @request_index = 0
13
+ end
14
+
15
+ def worker
16
+ worker_class.constantize.new
17
+ end
9
18
 
10
- reset
19
+ def process_response(qbxml_response, response, session, advance)
20
+ QBWC.logger.info "Processing response."
21
+ completed_request = requests[request_index]
22
+ advance_next_request if advance
23
+ QBWC.logger.info "Job '#{name}' received response: '#{qbxml_response}'." if QBWC.log_requests_and_responses
24
+ worker.handle_response(response, session, self, completed_request, data)
11
25
  end
12
26
 
13
- def set_response_proc(&block)
14
- @response_proc = block
27
+ def advance_next_request
28
+ new_index = request_index + 1
29
+ QBWC.logger.info "Job '#{name}' advancing to request #'#{new_index}'."
30
+ self.request_index = new_index
15
31
  end
16
32
 
17
33
  def enable
18
- @enabled = true
34
+ self.enabled = true
19
35
  end
20
36
 
21
37
  def disable
22
- @enabled = false
38
+ self.enabled = false
39
+ end
40
+
41
+ def pending?
42
+ if !enabled?
43
+ QBWC.logger.info "Job '#{name}' not enabled."
44
+ return false
45
+ end
46
+ sr = worker.should_run?(self)
47
+ QBWC.logger.info "Job '#{name}' should_run?: #{sr}."
48
+ return sr
23
49
  end
24
50
 
25
51
  def enabled?
26
52
  @enabled
27
53
  end
28
54
 
29
- def next
30
- @request_gen.alive? ? @request_gen.resume : nil
55
+ def requests
56
+ @requests
31
57
  end
32
58
 
33
- def reset
34
- @request_gen = new_request_generator
59
+ def requests=(r)
60
+ @requests = r
35
61
  end
36
62
 
37
- private
63
+ def data
64
+ @data
65
+ end
38
66
 
39
- def new_request_generator
40
- Fiber.new { request_queue.each { |r| Fiber.yield r }; nil }
67
+ def data=(d)
68
+ @data = d
41
69
  end
42
70
 
43
- def request_queue
44
- QBWC::Request.from_array(@requests.call, @response_proc )
71
+ def request_index
72
+ @request_index
73
+ end
74
+
75
+ def request_index=(ri)
76
+ @request_index = ri
77
+ end
78
+
79
+ def requests_provided_when_job_added
80
+ @requests_provided_when_job_added
81
+ end
82
+
83
+ def requests_provided_when_job_added=(value)
84
+ @requests_provided_when_job_added = value
85
+ end
86
+
87
+ def next_request
88
+ # Generate and save the requests to run when starting the job.
89
+ if (requests.nil? || requests.empty?) && ! self.requests_provided_when_job_added
90
+ r = worker.requests(self)
91
+ r = [r] unless r.nil? || r.is_a?(Array)
92
+ self.requests = r
93
+ end
94
+
95
+ QBWC.logger.info("Requests available are '#{requests}'.") if QBWC.log_requests_and_responses
96
+ ri = request_index
97
+ QBWC.logger.info("Request index is '#{ri}'.")
98
+ return nil if ri.nil? || requests.nil? || ri >= requests.length
99
+ nr = requests[ri]
100
+ QBWC.logger.info("Next request is '#{nr}'.") if QBWC.log_requests_and_responses
101
+ return QBWC::Request.new(nr)
102
+ end
103
+ alias :next :next_request # Deprecated method name 'next'
104
+
105
+ def reset
106
+ self.request_index = 0
107
+ self.requests = [] unless self.requests_provided_when_job_added
45
108
  end
46
109
 
47
110
  end
@@ -0,0 +1,8 @@
1
+ require 'wash_out'
2
+
3
+ module QBWC
4
+ class Railtie < ::Rails::Railtie
5
+ config.wash_out.parser = :nokogiri
6
+ config.wash_out.namespace = 'http://developer.intuit.com/'
7
+ end
8
+ end
@@ -1,46 +1,37 @@
1
1
  class QBWC::Request
2
2
 
3
3
  attr_reader :request, :response_proc
4
- attr_accessor :response, :error
5
4
 
6
- def initialize(request, response_proc)
5
+ def initialize(request)
7
6
  #Handle Cases for a request passed in as a Hash or String
8
7
  #If it's a hash verify that it is properly wrapped with qbxml_msg_rq and xml_attributes for on_error events
9
8
  #Allow strings of QBXML to be passed in directly.
10
9
  case
11
10
  when request.is_a?(Hash)
12
-
13
- unless request.keys.include?(:qbxml_msgs_rq)
14
- wrapped_request = { :qbxml_msgs_rq => {:xml_attributes => {"onError"=> QBWC::on_error } } }
15
- wrapped_request[:qbxml_msgs_rq] = wrapped_request[:qbxml_msgs_rq].merge(request)
16
- request = wrapped_request
17
- end
18
-
19
- @request = QBWC.parser.hash_to_qbxml(request)
11
+ request = self.class.wrap_request(request)
12
+ @request = QBWC.parser.to_qbxml(request, {:validate => true})
20
13
  when request.is_a?(String)
21
14
  @request = request
15
+ else
16
+ raise "Request '#{request}' must be a Hash or a String."
22
17
  end
23
- @response_proc = response_proc
24
- end
25
-
26
- def process_response
27
- @response_proc && @response && @response_proc.call(response)
28
18
  end
29
19
 
30
20
  def to_qbxml
31
- QBWC.parser.hash_to_qbxml(request)
21
+ QBWC.parser.to_qbxml(request)
32
22
  end
33
23
 
34
24
  def to_hash
35
- QBWC.parser.qbxml_to_hash @request.to_s
25
+ hash = QBWC.parser.from_qbxml(@request.to_s)["qbxml"]["qbxml_msgs_rq"]
26
+ hash.except('xml_attributes')
36
27
  end
37
28
 
38
- class << self
39
-
40
- def from_array(requests, response_proc)
41
- Array(requests).map { |r| new(r, response_proc) }
42
- end
43
-
29
+ # Wrap a Hash request with qbxml_msgs_rq, if it's not already.
30
+ def self.wrap_request(request)
31
+ return request if request.keys.include?(:qbxml_msgs_rq)
32
+ wrapped_request = { :qbxml_msgs_rq => {:xml_attributes => {"onError"=> QBWC::on_error } } }
33
+ wrapped_request[:qbxml_msgs_rq] = wrapped_request[:qbxml_msgs_rq].merge(request)
34
+ return wrapped_request
44
35
  end
45
36
 
46
37
  end
@@ -1,115 +1,143 @@
1
1
  class QBWC::Session
2
- include Enumerable
3
2
 
4
- attr_reader :current_job, :current_request, :saved_requests, :progress
5
- attr_reader :qbwc_iterator_queue, :qbwc_iterating
3
+ attr_reader :user, :company, :ticket, :progress
4
+ attr_accessor :error, :status_code, :status_severity
6
5
 
7
6
  @@session = nil
8
7
 
9
- def initialize
8
+ def self.get(ticket)
9
+ @@session
10
+ end
11
+
12
+ def initialize(user = nil, company = nil, ticket = nil)
13
+ @user = user
14
+ @company = company
10
15
  @current_job = nil
11
- @current_request = nil
12
- @saved_requests = []
16
+ @error = nil
17
+ @progress = 0
18
+ @iterator_id = nil
19
+ @initial_job_count = pending_jobs.length
13
20
 
14
- @qbwc_iterator_queue = []
15
- @qbwc_iterating = false
21
+ @ticket = ticket || Digest::SHA1.hexdigest("#{Rails.application.config.secret_token}#{Time.now.to_i}")
16
22
 
17
23
  @@session = self
18
- reset
24
+ reset(ticket.nil?)
25
+ end
26
+
27
+ def response_is_error?
28
+ self.error && self.status_severity == 'Error'
19
29
  end
20
30
 
21
- def reset
22
- @progress = QBWC.jobs.blank? ? 100 : 0
23
- enabled_jobs.map { |j| j.reset }
24
- @requests = build_request_generator(enabled_jobs)
31
+ def error_and_stop_requested?
32
+ response_is_error? && QBWC::on_error == 'stopOnError'
25
33
  end
26
34
 
27
35
  def finished?
28
- @progress == 100
36
+ self.progress == 100
37
+ end
38
+
39
+ def next_request
40
+ if current_job.nil? || error_and_stop_requested?
41
+ self.progress = 100
42
+ return nil
43
+ end
44
+ until (request = current_job.next_request) do
45
+ pending_jobs.shift
46
+ reset(true) or break
47
+ end
48
+ jobs_completed = @initial_job_count - pending_jobs.length
49
+ self.progress = ((jobs_completed.to_f / @initial_job_count.to_f ) * 100).to_i
50
+ request
29
51
  end
52
+ alias :next :next_request # Deprecated method name 'next'
53
+
54
+ def current_request
55
+ request = self.next_request
56
+ if request && self.iterator_id.present?
57
+ request = request.to_hash
58
+ request.delete('xml_attributes')
59
+ request.values.first['xml_attributes'] = {'iterator' => 'Continue', 'iteratorID' => self.iterator_id}
60
+ request = QBWC::Request.new(request)
61
+ end
62
+ request
63
+ end
64
+
65
+ def request_to_send
66
+ current_job_name = current_job.name
67
+ request = current_request.try(:request) || ''
68
+ QBWC.logger.info("Sending request from job #{current_job_name}")
69
+ QBWC.logger.info(request) if QBWC.log_requests_and_responses
30
70
 
31
- def next
32
- @requests.alive? ? @requests.resume : nil
71
+ request
33
72
  end
34
73
 
35
74
  def response=(qbxml_response)
36
75
  begin
37
- @current_request.response = QBWC.parser.qbxml_to_hash(qbxml_response)
38
- parse_response_header(@current_request.response)
39
-
40
- if QBWC.delayed_processing
41
- @saved_requests << @current_request
42
- else
43
- @current_request.process_response
76
+ QBWC.logger.info 'Parsing response.'
77
+ unless qbxml_response.nil?
78
+ response = QBWC.parser.from_qbxml(qbxml_response)["qbxml"]["qbxml_msgs_rs"].except("xml_attributes")
79
+ response = response[response.keys.first]
80
+ parse_response_header(response)
44
81
  end
82
+ self.current_job.process_response(qbxml_response, response, self, iterator_id.blank?) unless self.current_job.nil?
83
+ self.next_request # search next request
84
+
45
85
  rescue => e
46
- puts "An error occured in QBWC::Session: #{e}"
47
- puts e
48
- puts e.backtrace
86
+ self.error = e.message
87
+ QBWC.logger.warn "An error occured in QBWC::Session: #{e.message}"
88
+ QBWC.logger.warn e.backtrace.join("\n")
49
89
  end
90
+ end
50
91
 
92
+ def save
51
93
  end
52
94
 
53
- def process_saved_responses
54
- @saved_requests.each { |r| r.process_response }
95
+ def destroy
96
+ self.freeze
97
+ @@session = nil
55
98
  end
56
99
 
57
- private
100
+ protected
58
101
 
59
- def enabled_jobs
60
- QBWC.jobs.values.select { |j| j.enabled? }
61
- end
102
+ attr_accessor :current_job, :iterator_id
103
+ attr_writer :progress
62
104
 
63
- def build_request_generator(jobs)
64
- Fiber.new do
65
- jobs.each do |j|
66
- @current_job = j
67
- while (r = next_request)
68
- @current_request = r
69
- Fiber.yield r
70
- end
71
- end
105
+ private
72
106
 
73
- @progress = 100
74
- nil
75
- end
107
+ def reset(reset_job = false)
108
+ self.current_job = pending_jobs.first
109
+ self.current_job.reset if reset_job && self.current_job
110
+ return self.current_job
76
111
  end
77
112
 
78
- def next_request
79
- (@qbwc_iterating == true && @qbwc_iterator_queue.shift) || @current_job.next
113
+ def pending_jobs
114
+ @pending_jobs ||= QBWC.pending_jobs(@company)
80
115
  end
81
116
 
82
117
  def parse_response_header(response)
83
- return unless response['xml_attributes']
118
+ QBWC.logger.info 'Parsing headers.'
84
119
 
85
- status_code, status_severity, status_message, iterator_remaining_count, iterator_id = \
86
- response['xml_attributes'].values_at('statusCode', 'statusSeverity', 'statusMessage',
87
- 'iteratorRemainingCount', 'iteratorID')
88
-
89
- if status_severity == 'Error' || status_code.to_i > 1 || response.keys.size <= 1
90
- @current_request.error = "QBWC ERROR: #{status_code} - #{status_message}"
91
- else
92
- if iterator_remaining_count.to_i > 0
93
- @qbwc_iterating = true
94
- new_request = @current_request.to_hash
95
- new_request.delete('xml_attributes')
96
- new_request.values.first['xml_attributes'] = {'iterator' => 'Continue', 'iteratorID' => iterator_id}
97
- @qbwc_iterator_queue << QBWC::Request.new(new_request, @current_request.response_proc)
98
- else
99
- @qbwc_iterating = false
100
- end
120
+ self.iterator_id = nil
121
+ self.error = nil
122
+ self.status_code = nil
123
+ self.status_severity = nil
124
+
125
+ if response.is_a? Array
126
+ response = response.find {|r| r.is_a?(Hash) && r['xml_attributes'] && r['xml_attributes']['statusCode'].to_i > 1} || response.first
101
127
  end
102
- end
128
+ return unless response.is_a?(Hash) && response['xml_attributes']
103
129
 
104
- class << self
130
+ @status_code, @status_severity, status_message, iterator_remaining_count, iterator_id = \
131
+ response['xml_attributes'].values_at('statusCode', 'statusSeverity', 'statusMessage',
132
+ 'iteratorRemainingCount', 'iteratorID')
133
+ QBWC.logger.info "Parsed headers. statusSeverity: '#{status_severity}'. statusCode: '#{@status_code}'"
105
134
 
106
- def new_or_unfinished
107
- (!@@session || @@session.finished?) ? new : @@session
108
- end
135
+ if @status_severity == 'Error' || @status_severity == 'Warn'
136
+ self.error = "QBWC #{@status_severity.upcase}: #{@status_code} - #{status_message}"
137
+ @status_severity == 'Error' ? QBWC.logger.error(self.error) : QBWC.logger.warn(self.error)
138
+ end
109
139
 
110
- end
140
+ self.iterator_id = iterator_id if iterator_remaining_count.to_i > 0 && @status_severity != 'Error'
111
141
 
112
- def self.session
113
- @@session
114
- end
142
+ end
115
143
  end