Commit 96149efabcc7275d6e2ac7453a119b76dc57b976
1 parent
0c0af54a
working query browse
Showing
9 changed files
with
244 additions
and
11 deletions
Show diff stats
Gemfile.lock
1 | PATH | 1 | PATH |
2 | remote: . | 2 | remote: . |
3 | specs: | 3 | specs: |
4 | - syspro (0.1.0) | 4 | + syspro-ruby (0.1.0) |
5 | faraday (~> 0.10) | 5 | faraday (~> 0.10) |
6 | 6 | ||
7 | GEM | 7 | GEM |
@@ -26,7 +26,7 @@ DEPENDENCIES | @@ -26,7 +26,7 @@ DEPENDENCIES | ||
26 | minitest (~> 5.0) | 26 | minitest (~> 5.0) |
27 | pry (~> 0.11) | 27 | pry (~> 0.11) |
28 | rake (~> 10.0) | 28 | rake (~> 10.0) |
29 | - syspro! | 29 | + syspro-ruby! |
30 | 30 | ||
31 | BUNDLED WITH | 31 | BUNDLED WITH |
32 | 1.16.1 | 32 | 1.16.1 |
lib/syspro.rb
@@ -18,6 +18,11 @@ require "syspro/util" | @@ -18,6 +18,11 @@ require "syspro/util" | ||
18 | require "syspro/version" | 18 | require "syspro/version" |
19 | 19 | ||
20 | require "syspro/api_operations/request" | 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 | module Syspro | 27 | module Syspro |
23 | @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest" | 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,13 +37,6 @@ module Syspro | ||
37 | def self.included(base) | 37 | def self.included(base) |
38 | base.extend(ClassMethods) | 38 | base.extend(ClassMethods) |
39 | end | 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 | end | 40 | end |
48 | end | 41 | end |
49 | end | 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 | + |
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,9 +178,9 @@ module Syspro | ||
178 | case e | 178 | case e |
179 | when Faraday::ClientError | 179 | when Faraday::ClientError |
180 | if e.response | 180 | if e.response |
181 | - handle_error_response(e.response, error_context) | 181 | + handle_error_response(e.response, context) |
182 | else | 182 | else |
183 | - handle_network_error(e, error_context, num_retries, api_base) | 183 | + handle_network_error(e, context, num_retries, api_base) |
184 | end | 184 | end |
185 | 185 | ||
186 | # Only handle errors when we know we can do so, and re-raise otherwise. | 186 | # Only handle errors when we know we can do so, and re-raise otherwise. |
@@ -193,6 +193,35 @@ module Syspro | @@ -193,6 +193,35 @@ module Syspro | ||
193 | resp | 193 | resp |
194 | end | 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 | def self.should_retry?(e, num_retries) | 225 | def self.should_retry?(e, num_retries) |
197 | return false if num_retries >= Syspro.max_network_retries | 226 | return false if num_retries >= Syspro.max_network_retries |
198 | 227 | ||
@@ -249,6 +278,26 @@ module Syspro | @@ -249,6 +278,26 @@ module Syspro | ||
249 | end | 278 | end |
250 | private :log_response_error | 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 | # RequestLogContext stores information about a request that's begin made so | 301 | # RequestLogContext stores information about a request that's begin made so |
253 | # that we can log certain information. It's useful because it means that we | 302 | # that we can log certain information. It's useful because it means that we |
254 | # don't have to pass around as many parameters. | 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 |