Commit 96149efabcc7275d6e2ac7453a119b76dc57b976
1 parent
0c0af54a
working query browse
Showing
9 changed files
with
244 additions
and
11 deletions
Show diff stats
Gemfile.lock
lib/syspro.rb
... | ... | @@ -18,6 +18,11 @@ require "syspro/util" |
18 | 18 | require "syspro/version" |
19 | 19 | |
20 | 20 | require "syspro/api_operations/request" |
21 | +require "syspro/api_operations/query" | |
22 | + | |
23 | +require "syspro/business_objects/combrw" | |
24 | + | |
25 | +require "syspro/business_objects/parsers/combrw_parser" | |
21 | 26 | |
22 | 27 | module Syspro |
23 | 28 | @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest" | ... | ... |
1 | +module Syspro | |
2 | + module ApiOperations | |
3 | + module Query | |
4 | + module ClassMethods | |
5 | + include Request | |
6 | + | |
7 | + def browse(params) | |
8 | + request(:get, "/Query/Browse", params) | |
9 | + end | |
10 | + | |
11 | + def fetch | |
12 | + end | |
13 | + | |
14 | + def query | |
15 | + end | |
16 | + | |
17 | + def find | |
18 | + end | |
19 | + | |
20 | + private | |
21 | + | |
22 | + def warn_on_opts_in_params(params) | |
23 | + Util::OPTS_USER_SPECIFIED.each do |opt| | |
24 | + if params.key?(opt) | |
25 | + $stderr.puts("WARNING: #{opt} should be in opts instead of params.") | |
26 | + end | |
27 | + end | |
28 | + end | |
29 | + end # ClassMethods | |
30 | + | |
31 | + def self.included(base) | |
32 | + base.extend(ClassMethods) | |
33 | + end | |
34 | + | |
35 | + protected | |
36 | + | |
37 | + def request(method, url, params = {}, opts = {}) | |
38 | + opts = @opts.merge(Util.normalize_opts(opts)) | |
39 | + Request.request(method, url, params, opts) | |
40 | + end | |
41 | + end | |
42 | + end | |
43 | +end | |
44 | + | ... | ... |
lib/syspro/api_operations/request.rb
... | ... | @@ -37,13 +37,6 @@ module Syspro |
37 | 37 | def self.included(base) |
38 | 38 | base.extend(ClassMethods) |
39 | 39 | end |
40 | - | |
41 | - protected | |
42 | - | |
43 | - def request(method, url, params = {}, opts = {}) | |
44 | - opts = @opts.merge(Util.normalize_opts(opts)) | |
45 | - self.class.request(method, url, params, opts) | |
46 | - end | |
47 | 40 | end |
48 | 41 | end |
49 | 42 | end | ... | ... |
1 | +require "syspro/business_objects/parsers/combrw_parser" | |
2 | +require "erb" | |
3 | + | |
4 | +module Syspro | |
5 | + module BusinessObjects | |
6 | + class ComBrw < ApiResource | |
7 | + include Syspro::ApiOperations::Query | |
8 | + include Syspro::BusinessObjects::Parsers | |
9 | + | |
10 | + attr_accessor :browse_name, :start_condition, :return_rows, :filters, | |
11 | + :table_name, :title, :columns | |
12 | + | |
13 | + def call(user_id) | |
14 | + xml_in = template.result(binding) | |
15 | + params = { "UserId" => user_id, "XmlIn" => xml_in } | |
16 | + resp = ComBrw.browse(params) | |
17 | + parse_response(resp) | |
18 | + end | |
19 | + | |
20 | + def template | |
21 | + ERB.new File.read(File.expand_path("schemas/combrw.xml.erb", File.dirname(__FILE__))), nil, "%" | |
22 | + end | |
23 | + | |
24 | + def parse_response(resp) | |
25 | + handle_errors(resp) | |
26 | + ComBrwParser.parse(resp[0].data) | |
27 | + end | |
28 | + | |
29 | + def handle_errors(resp) | |
30 | + body = resp[0].http_body | |
31 | + if body.match(/^(ERROR)/) | |
32 | + raise SysproError, body | |
33 | + end | |
34 | + end | |
35 | + end | |
36 | + end | |
37 | +end | |
38 | + | ... | ... |
lib/syspro/business_objects/parsers/combrw_parser.rb
0 โ 100644
1 | +module Syspro | |
2 | + module BusinessObjects | |
3 | + module Parsers | |
4 | + class ComBrwParser | |
5 | + | |
6 | + def self.parse(doc) | |
7 | + next_prev_key = doc.first_element_child.xpath("NextPrevKey") | |
8 | + next_prev_key_obj = next_prev_key.children.map { |el| | |
9 | + if el.name == "text" | |
10 | + next | |
11 | + end | |
12 | + { | |
13 | + name: el.name, | |
14 | + text: el.text | |
15 | + } | |
16 | + }.compact | |
17 | + | |
18 | + header_details = doc.first_element_child.xpath("HeaderDetails") | |
19 | + header_details_obj = header_details.children.map { |el| | |
20 | + if el.name == "text" | |
21 | + next | |
22 | + end | |
23 | + { | |
24 | + name: el.name, | |
25 | + text: el.text | |
26 | + } | |
27 | + }.compact | |
28 | + | |
29 | + rows = doc.first_element_child.xpath('Row') | |
30 | + rows_obj = rows.map { |el| | |
31 | + el.elements.map { |el| | |
32 | + { | |
33 | + name: el.name, | |
34 | + value: el.xpath('Value').text, | |
35 | + data_type: el.xpath('DataType').text | |
36 | + } | |
37 | + } | |
38 | + }.flatten(1).compact | |
39 | + | |
40 | + BrowseObject.new( | |
41 | + doc.first_element_child.xpath("Title").text, | |
42 | + rows_obj, | |
43 | + next_prev_key_obj, | |
44 | + header_details_obj | |
45 | + ) | |
46 | + end | |
47 | + | |
48 | + BrowseObject = Struct.new(:title, :rows, :next_prev_key, :header_details) | |
49 | + end | |
50 | + end | |
51 | + end | |
52 | +end | |
53 | + | ... | ... |
lib/syspro/business_objects/schemas/combrw.xml.erb
0 โ 100644
1 | +<?xml version="1.0" encoding="Windows-1252"?> | |
2 | +<!-- Copyright 1994-2014 SYSPRO Ltd.--> | |
3 | +<!-- | |
4 | + Sample XML for the Generic Browse Object | |
5 | +--> | |
6 | +<Browse xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xsd:noNamespaceSchemaLocation="COMBRW.XSD"> | |
7 | + <BrowseName><%= @browse_name %></BrowseName> | |
8 | + <StartAtKey/> | |
9 | + <StartCondition><%= @start_condition %></StartCondition> | |
10 | + <ReturnRows><%= @return_rows %></ReturnRows> | |
11 | + <% for @filter in @filters %> | |
12 | + <Filter> | |
13 | + <ColumnFilterName><%= @filter[:name] %></ColumnFilterName> | |
14 | + <ColumnFilterValue><%= @filter[:value] %></ColumnFilterValue> | |
15 | + </Filter> | |
16 | + <% end %> | |
17 | + <BrowseDetails> | |
18 | + <TableName><%= @table_name %></TableName> | |
19 | + <Title><%= @title %></Title> | |
20 | + <% for @column in @columns %> | |
21 | + <Column> | |
22 | + <% unless @column[:name].nil? %><ColumnName><%= @column[:name] %></ColumnName><% end %> | |
23 | + <% unless @column[:description].nil? %><ColumnDescription><%= @column[:description] %></ColumnDescription><% end %> | |
24 | + <% unless @column[:key].nil? %><ColumnKey><%= @column[:key] %></ColumnKey><% end %> | |
25 | + </Column> | |
26 | + <% end %> | |
27 | + </BrowseDetails> | |
28 | +</Browse> | |
29 | + | ... | ... |
lib/syspro/syspro_client.rb
... | ... | @@ -178,9 +178,9 @@ module Syspro |
178 | 178 | case e |
179 | 179 | when Faraday::ClientError |
180 | 180 | if e.response |
181 | - handle_error_response(e.response, error_context) | |
181 | + handle_error_response(e.response, context) | |
182 | 182 | else |
183 | - handle_network_error(e, error_context, num_retries, api_base) | |
183 | + handle_network_error(e, context, num_retries, api_base) | |
184 | 184 | end |
185 | 185 | |
186 | 186 | # Only handle errors when we know we can do so, and re-raise otherwise. |
... | ... | @@ -193,6 +193,35 @@ module Syspro |
193 | 193 | resp |
194 | 194 | end |
195 | 195 | |
196 | + def handle_network_error(e, context, num_retries, api_base = nil) | |
197 | + Util.log_error("Syspro network error", | |
198 | + error_message: e.message, | |
199 | + request_id: context.request_id) | |
200 | + | |
201 | + case e | |
202 | + when Faraday::ConnectionFailed | |
203 | + message = "Unexpected error communicating when trying to connect to Syspro." | |
204 | + | |
205 | + when Faraday::SSLError | |
206 | + message = "Could not establish a secure connection to Syspro." | |
207 | + | |
208 | + when Faraday::TimeoutError | |
209 | + api_base ||= Syspro.api_base | |
210 | + message = "Could not connect to Syspro (#{api_base}). " \ | |
211 | + "Please check your internet connection and try again. " \ | |
212 | + "If this problem persists, you should check your Syspro service status." | |
213 | + | |
214 | + else | |
215 | + message = "Unexpected error communicating with Syspro. " \ | |
216 | + "If this problem persists, talk to your Syspro implementation team." | |
217 | + | |
218 | + end | |
219 | + | |
220 | + message += " Request was retried #{num_retries} times." if num_retries > 0 | |
221 | + | |
222 | + raise ApiConnectionError, message + "\n\n(Network error: #{e.message})" | |
223 | + end | |
224 | + | |
196 | 225 | def self.should_retry?(e, num_retries) |
197 | 226 | return false if num_retries >= Syspro.max_network_retries |
198 | 227 | |
... | ... | @@ -249,6 +278,26 @@ module Syspro |
249 | 278 | end |
250 | 279 | private :log_response_error |
251 | 280 | |
281 | + def handle_error_response(http_resp, context) | |
282 | + begin | |
283 | + resp = SysproResponse.from_faraday_hash(http_resp) | |
284 | + error_data = resp.data[:error] | |
285 | + | |
286 | + raise SysproError, "Indeterminate error" unless error_data | |
287 | + rescue Nokogiri::XML::SyntaxError, SysproError | |
288 | + raise general_api_error(http_resp[:status], http_resp[:body]) | |
289 | + end | |
290 | + | |
291 | + error = if error_data.is_a?(String) | |
292 | + specific_oauth_error(resp, error_data, context) | |
293 | + else | |
294 | + specific_api_error(resp, error_data, context) | |
295 | + end | |
296 | + | |
297 | + error.response = resp | |
298 | + raise(error) | |
299 | + end | |
300 | + | |
252 | 301 | # RequestLogContext stores information about a request that's begin made so |
253 | 302 | # that we can log certain information. It's useful because it means that we |
254 | 303 | # don't have to pass around as many parameters. | ... | ... |
1 | +require "test_helper" | |
2 | + | |
3 | +class QueryTest < Minitest::Test | |
4 | + def test_query | |
5 | + user_id = Syspro::Logon.logon("wland", "piperita2016", "L", "") | |
6 | + | |
7 | + combrw = Syspro::BusinessObjects::ComBrw.new | |
8 | + combrw.browse_name = "InvMaster" | |
9 | + combrw.start_condition = "" | |
10 | + combrw.return_rows = 5 | |
11 | + combrw.filters = [] | |
12 | + combrw.table_name = "InvMaster" | |
13 | + combrw.title = "StockCodes" | |
14 | + combrw.columns = [ | |
15 | + {name: "StockCode"} | |
16 | + ] | |
17 | + | |
18 | + query_result = combrw.call(user_id.guid) | |
19 | + | |
20 | + refute_nil query_result | |
21 | + end | |
22 | +end | ... | ... |