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 | ... | ... |