Commit 291179ed2e407c287e1359c1add0a9900164fa69

Authored by Isaac Lewis
Committed by GitHub
2 parents 9a47698f a3292c71

Merge pull request #2 from wildland/query

Query Endpoint
.rubocop.yml 0 โ†’ 100644
  1 +AllCops:
  2 + Exclude:
  3 + - Gemfile*
  4 + - syspro-ruby.gemspec
... ...
CHANGELOG.md 0 โ†’ 100644
  1 +# Changelog
  2 +All notable changes to this project will be documented in this file.
  3 +
  4 +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
  5 +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
  6 +
  7 +## [Unreleased]
  8 +### Added
  9 +### Changed
  10 +### Deprecated
  11 +### Removed
  12 +### Fixed
  13 +### Security
  14 +
  15 +## 0.1.0 - 2018-04-06
  16 +### Added
  17 +- CHANGELOG file
  18 +- `rubocop` gem
  19 +- `vcr` gem
  20 +- Query Endpoint
... ...
1   -source "https://rubygems.org"
  1 +# frozen_string_literal: true
2 2  
3   -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
  3 +source 'https://rubygems.org'
  4 +
  5 +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4 6  
5 7 gemspec
... ...
Gemfile.lock
1 1 PATH
2 2 remote: .
3 3 specs:
4   - syspro (0.1.0)
  4 + syspro-ruby (1.0.0.alpha.1)
5 5 faraday (~> 0.10)
  6 + nokogiri (~> 1.8.2)
6 7  
7 8 GEM
8 9 remote: https://rubygems.org/
9 10 specs:
  11 + addressable (2.5.2)
  12 + public_suffix (>= 2.0.2, < 4.0)
  13 + ast (2.4.0)
10 14 coderay (1.1.2)
  15 + crack (0.4.3)
  16 + safe_yaml (~> 1.0.0)
11 17 faraday (0.14.0)
12 18 multipart-post (>= 1.2, < 3)
  19 + hashdiff (0.3.7)
13 20 method_source (0.9.0)
  21 + mini_portile2 (2.3.0)
  22 + minispec-metadata (2.0.0)
  23 + minitest
14 24 minitest (5.11.3)
  25 + minitest-vcr (1.4.0)
  26 + minispec-metadata (~> 2.0)
  27 + minitest (>= 4.7.5)
  28 + vcr (>= 2.9)
15 29 multipart-post (2.0.0)
  30 + nokogiri (1.8.2)
  31 + mini_portile2 (~> 2.3.0)
  32 + parallel (1.12.1)
  33 + parser (2.5.0.5)
  34 + ast (~> 2.4.0)
  35 + powerpack (0.1.1)
16 36 pry (0.11.3)
17 37 coderay (~> 1.1.0)
18 38 method_source (~> 0.9.0)
  39 + public_suffix (3.0.2)
  40 + rainbow (3.0.0)
19 41 rake (10.5.0)
  42 + rubocop (0.54.0)
  43 + parallel (~> 1.10)
  44 + parser (>= 2.5)
  45 + powerpack (~> 0.1)
  46 + rainbow (>= 2.2.2, < 4.0)
  47 + ruby-progressbar (~> 1.7)
  48 + unicode-display_width (~> 1.0, >= 1.0.1)
  49 + ruby-progressbar (1.9.0)
  50 + safe_yaml (1.0.4)
  51 + unicode-display_width (1.3.0)
  52 + vcr (4.0.0)
  53 + webmock (3.3.0)
  54 + addressable (>= 2.3.6)
  55 + crack (>= 0.3.2)
  56 + hashdiff
20 57  
21 58 PLATFORMS
22 59 ruby
... ... @@ -24,9 +61,12 @@ PLATFORMS
24 61 DEPENDENCIES
25 62 bundler (~> 1.16)
26 63 minitest (~> 5.0)
  64 + minitest-vcr (~> 1.4.0)
27 65 pry (~> 0.11)
28 66 rake (~> 10.0)
29   - syspro!
  67 + rubocop (~> 0.54.0)
  68 + syspro-ruby!
  69 + webmock (~> 3.3.0)
30 70  
31 71 BUNDLED WITH
32 72 1.16.1
... ...
README.md
... ... @@ -72,6 +72,103 @@ logged_off = Syspro::Logoff.logoff(guid)
72 72 ```
73 73 `logged_off` will be `true` if the user has been successfully logged off, and will contain an error string if an error has occured.
74 74  
  75 +### Query
  76 +
  77 +#### Browse
  78 +Browse returns a paginated view of a particular table.
  79 +
  80 +This is an example using the generic Browse Business Object, `COMBRW`.
  81 +```rb
  82 +combrw = Syspro::BusinessObject::ComBrw.new
  83 +combrw.browse_name = "InvMaster"
  84 +combrw.start_condition = ""
  85 +combrw.return_rows = 5
  86 +combrw.filters = []
  87 +combrw.table_name = "InvMaster"
  88 +combrw.title = "StockCodes"
  89 +combrw.columns = [
  90 + {name: "StockCode"}
  91 +]
  92 +
  93 +browse_result = combrw.call(user_id.guid)
  94 +```
  95 +
  96 +`browse_result` will be a BrowseObject, which has the following structure:
  97 +
  98 +```rb
  99 +{
  100 + title: "Title",
  101 + rows: [ { name: "", value: "", data_type: "" } ],
  102 + next_prev_key: { name: "", text: "" },
  103 + header_details: { name: "", text: "" }
  104 +}
  105 +```
  106 +
  107 +#### Query
  108 +
  109 +Query gives control over joins between multiple tables over a single Business Object.
  110 +
  111 +This is an example using the generic Query Business Object, `COMFND`.
  112 +
  113 +```rb
  114 +comfnd = Syspro::BusinessObjects::ComFnd.new
  115 +comfnd.table_name = "InvMaster"
  116 +comfnd.return_rows = 5
  117 +comfnd.columns = [
  118 + {
  119 + name: "StockCode"
  120 + }
  121 +]
  122 +comfnd.expressions = [
  123 + {
  124 + andor: "And",
  125 + column: "StockCode",
  126 + condition: "EQ",
  127 + value: "02"
  128 + }
  129 +]
  130 +comfnd.order_by = "StockCode"
  131 +
  132 +query_result = comfnd.call(user_id.guid)
  133 +```
  134 +
  135 +This will return a QueryObject, which looks like this:
  136 +
  137 +```rb
  138 +{
  139 + header_details: { name: "", text: "" },
  140 + rows: [ { name: "", value: "" } ],
  141 + row_count: 1
  142 +}
  143 +```
  144 +
  145 +#### Fetch
  146 +
  147 +Fetch selects the `TOP 1` of the query.
  148 +
  149 +This is an example using the generic Fetch Business Object, `COMFCH`.
  150 +
  151 +```rb
  152 +comfch = Syspro::BusinessObjects::ComFch.new
  153 +comfch.table_name = "InvMaster"
  154 +comfch.key = "02"
  155 +comfch.optional_keys = []
  156 +comfch.full_key_provided = false
  157 +comfch.default_type = ""
  158 +comfch.espresso_fetch = true
  159 +
  160 +fetch_result = comfch.call(user_id.guid)
  161 +```
  162 +
  163 +This will return a FetchObject, with the following structure:
  164 +
  165 +```rb
  166 +{
  167 + table_name: "",
  168 + columns: [ { name: "", value: "" } ]
  169 +}
  170 +```
  171 +
75 172 ## Development
76 173  
77 174 After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
... ... @@ -80,6 +177,8 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
80 177  
81 178 ## Contributing
82 179  
  180 +Run `bundle exec rake` and ensure everything looks good.
  181 +
83 182 Bug reports and pull requests are welcome on GitHub at https://github.com/ike/syspro-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
84 183  
85 184 ## License
... ... @@ -88,4 +187,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
88 187  
89 188 ## Code of Conduct
90 189  
91   -Everyone interacting in the Syspro projectโ€™s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ike/syspro-ruby/blob/master/CODE_OF_CONDUCT.md).
  190 +Everyone interacting in the Syspro projectโ€™s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/wildland/code-of-conduct).
... ...
Rakefile
1   -require "bundler/gem_tasks"
2   -require "rake/testtask"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'bundler/gem_tasks'
  4 +require 'rake/testtask'
  5 +require 'rubocop/rake_task'
3 6  
4 7 Rake::TestTask.new(:test) do |t|
5   - t.libs << "test"
6   - t.libs << "lib"
7   - t.test_files = FileList["test/**/*_test.rb"]
  8 + t.libs << 'test'
  9 + t.libs << 'lib'
  10 + t.test_files = FileList['test/**/*_test.rb']
8 11 end
9 12  
10   -task :default => :test
  13 +task default: %i[test rubocop]
  14 +
  15 +RuboCop::RakeTask.new(:rubocop) do |t|
  16 + t.options = ['--display-cop-names']
  17 +end
... ...
bin/console
1 1 #!/usr/bin/env ruby
  2 +# frozen_string_literal: true
2 3  
3   -require "bundler/setup"
4   -require "syspro"
5   -require "pry"
  4 +require 'bundler/setup'
  5 +require 'syspro'
  6 +require 'pry'
6 7  
7 8 Pry.start
... ...
lib/syspro.rb
1   -require "cgi"
2   -require "faraday"
3   -require "json"
4   -require "logger"
5   -require "openssl"
6   -
7   -require "syspro/api_resource"
8   -require "syspro/errors"
9   -require "syspro/get_logon_profile"
10   -require "syspro/get_version"
11   -require "syspro/logoff"
12   -require "syspro/logon"
13   -require "syspro/syspro_client"
14   -require "syspro/singleton_api_resource"
15   -require "syspro/syspro_object"
16   -require "syspro/syspro_response"
17   -require "syspro/util"
18   -require "syspro/version"
19   -
20   -require "syspro/api_operations/request"
21   -
  1 +# frozen_string_literal: true
  2 +
  3 +require 'cgi'
  4 +require 'faraday'
  5 +require 'json'
  6 +require 'logger'
  7 +require 'openssl'
  8 +
  9 +require 'syspro/api_resource'
  10 +require 'syspro/errors'
  11 +require 'syspro/get_logon_profile'
  12 +require 'syspro/get_version'
  13 +require 'syspro/logoff'
  14 +require 'syspro/logon'
  15 +require 'syspro/syspro_client'
  16 +require 'syspro/singleton_api_resource'
  17 +require 'syspro/syspro_object'
  18 +require 'syspro/syspro_response'
  19 +require 'syspro/util'
  20 +require 'syspro/version'
  21 +
  22 +require 'syspro/api_operations/request'
  23 +require 'syspro/api_operations/query'
  24 +
  25 +require 'syspro/business_objects/combrw'
  26 +require 'syspro/business_objects/comfch'
  27 +require 'syspro/business_objects/comfnd'
  28 +
  29 +require 'syspro/business_objects/parsers/combrw_parser'
  30 +require 'syspro/business_objects/parsers/comfch_parser'
  31 +require 'syspro/business_objects/parsers/comfnd_parser'
  32 +
  33 +# Main Module
22 34 module Syspro
23   - @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest"
  35 + @api_base = 'http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest'
24 36  
25 37 @open_timeout = 30
26 38 @read_timeout = 80
... ... @@ -60,14 +72,13 @@ module Syspro
60 72  
61 73 def self.log_level=(val)
62 74 # Backwards compatibility for values that we briefly allowed
63   - if val == "debug"
64   - val = LEVEL_DEBUG
65   - elsif val == "info"
66   - val = LEVEL_INFO
67   - end
68   -
  75 + val = LEVEL_DEBUG if val == 'debug'
  76 + val = LEVEL_INFO if val == 'info'
69 77 if !val.nil? && ![LEVEL_DEBUG, LEVEL_ERROR, LEVEL_INFO].include?(val)
70   - raise ArgumentError, "log_level should only be set to `nil`, `debug` or `info`"
  78 + raise(
  79 + ArgumentError,
  80 + 'log_level should only be set to `nil`, `debug` or `info`'
  81 + )
71 82 end
72 83 @log_level = val
73 84 end
... ... @@ -95,5 +106,5 @@ module Syspro
95 106 @max_network_retries = val.to_i
96 107 end
97 108  
98   - Syspro.log_level = ENV["SYSPRO_LOG"] unless ENV["SYSPRO_LOG"].nil?
  109 + Syspro.log_level = ENV['SYSPRO_LOG'] unless ENV['SYSPRO_LOG'].nil?
99 110 end
... ...
lib/syspro/api_operations/query.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +module Syspro
  4 + module ApiOperations
  5 + module Query
  6 + module ClassMethods
  7 + def browse(params)
  8 + request(:get, '/Query/Browse', params)
  9 + end
  10 +
  11 + def fetch(params)
  12 + request(:get, '/Query/Fetch', params)
  13 + end
  14 +
  15 + def query(params)
  16 + request(:get, '/Query/Query', params)
  17 + end
  18 +
  19 + def find; end
  20 +
  21 + private
  22 +
  23 + def warn_on_opts_in_params(params)
  24 + Util::OPTS_USER_SPECIFIED.each do |opt|
  25 + if params.key?(opt)
  26 + warn("WARNING: #{opt} should be in opts instead of params.")
  27 + end
  28 + end
  29 + end
  30 + end # ClassMethods
  31 +
  32 + def self.included(base)
  33 + base.extend(ClassMethods)
  34 + end
  35 +
  36 + protected
  37 +
  38 + def request(method, url, params = {}, opts = {})
  39 + opts = @opts.merge(Util.normalize_opts(opts))
  40 + Request.request(method, url, params, opts)
  41 + end
  42 + end
  43 + end
  44 +end
... ...
lib/syspro/api_operations/request.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2 4 module ApiOperations
3 5 module Request
... ... @@ -28,7 +30,7 @@ module Syspro
28 30 def warn_on_opts_in_params(params)
29 31 Util::OPTS_USER_SPECIFIED.each do |opt|
30 32 if params.key?(opt)
31   - $stderr.puts("WARNING: #{opt} should be in opts instead of params.")
  33 + warn("WARNING: #{opt} should be in opts instead of params.")
32 34 end
33 35 end
34 36 end
... ... @@ -37,14 +39,6 @@ module Syspro
37 39 def self.included(base)
38 40 base.extend(ClassMethods)
39 41 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 42 end
48 43 end
49 44 end
50   -
... ...
lib/syspro/api_resource.rb
1   -require "syspro/syspro_object"
2   -require "syspro/api_operations/request"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'syspro/syspro_object'
  4 +require 'syspro/api_operations/request'
3 5  
4 6 module Syspro
5 7 class ApiResource < SysproObject
6 8 include Syspro::ApiOperations::Request
7 9  
8 10 def self.class_name
9   - name.split("::")[-1]
  11 + name.split('::')[-1]
10 12 end
11 13  
12 14 def self.resource_url
13 15 if self == ApiResource
14   - raise NotImplementedError, "APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)"
  16 + raise NotImplementedError, 'APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)'
15 17 end
16 18 "/#{CGI.escape(class_name.downcase)}"
17 19 end
... ...
lib/syspro/business_objects/combrw.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +require 'syspro/business_objects/parsers/combrw_parser'
  4 +require 'erb'
  5 +
  6 +module Syspro
  7 + module BusinessObjects
  8 + class ComBrw < ApiResource
  9 + include Syspro::ApiOperations::Query
  10 + include Syspro::BusinessObjects::Parsers
  11 +
  12 + attr_accessor :browse_name, :start_condition, :return_rows, :filters,
  13 + :table_name, :title, :columns
  14 +
  15 + def call(user_id)
  16 + xml_in = template.result(binding)
  17 + params = { 'UserId' => user_id, 'XmlIn' => xml_in }
  18 + resp = ComBrw.browse(params)
  19 + parse_response(resp)
  20 + end
  21 +
  22 + def template
  23 + ERB.new(
  24 + File.read(
  25 + File.expand_path('schemas/combrw.xml.erb', File.dirname(__FILE__))
  26 + ),
  27 + nil,
  28 + '%'
  29 + )
  30 + end
  31 +
  32 + def parse_response(resp)
  33 + handle_errors(resp)
  34 + parser = ComBrwParser.new(resp[0].data)
  35 + parser.parse
  36 + end
  37 +
  38 + def handle_errors(resp)
  39 + body = resp[0].http_body
  40 + raise SysproError, body if body =~ /^(ERROR)/
  41 + end
  42 + end
  43 + end
  44 +end
... ...
lib/syspro/business_objects/comfch.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +require 'syspro/business_objects/parsers/comfch_parser'
  4 +require 'erb'
  5 +
  6 +module Syspro
  7 + module BusinessObjects
  8 + class ComFch < ApiResource
  9 + include Syspro::ApiOperations::Query
  10 + include Syspro::BusinessObjects::Parsers
  11 +
  12 + attr_accessor :table_name, :key, :optional_keys, :full_key_provided,
  13 + :default_type, :espresso_fetch
  14 +
  15 + def call(user_id)
  16 + xml_in = template.result(binding)
  17 + params = { 'UserId' => user_id, 'XmlIn' => xml_in }
  18 + resp = ComFch.fetch(params)
  19 + parse_response(resp)
  20 + end
  21 +
  22 + def template
  23 + ERB.new(
  24 + File.read(
  25 + File.expand_path('schemas/comfch.xml.erb', File.dirname(__FILE__))
  26 + ),
  27 + nil,
  28 + '%'
  29 + )
  30 + end
  31 +
  32 + def parse_response(resp)
  33 + handle_errors(resp)
  34 + parser = ComFchParser.new(resp[0].data)
  35 + parser.parse
  36 + end
  37 +
  38 + def handle_errors(resp)
  39 + body = resp[0].http_body
  40 + raise SysproError, body if body =~ /^(ERROR)/
  41 + end
  42 + end
  43 + end
  44 +end
... ...
lib/syspro/business_objects/comfnd.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +require 'syspro/business_objects/parsers/comfnd_parser'
  4 +require 'erb'
  5 +
  6 +module Syspro
  7 + module BusinessObjects
  8 + class ComFnd < ApiResource
  9 + include Syspro::ApiOperations::Query
  10 + include Syspro::BusinessObjects::Parsers
  11 +
  12 + attr_accessor :table_name, :return_rows, :columns, :expressions,
  13 + :order_by
  14 +
  15 + def call(user_id)
  16 + xml_in = template.result(binding)
  17 + business_object = 'COMFND'
  18 + params = { 'UserId' => user_id, 'BusinessObject' => business_object, 'XmlIn' => xml_in }
  19 + resp = ComFnd.query(params)
  20 + parse_response(resp)
  21 + end
  22 +
  23 + def template
  24 + ERB.new File.read(File.expand_path('schemas/comfnd.xml.erb', File.dirname(__FILE__))), nil, '%'
  25 + end
  26 +
  27 + def parse_response(resp)
  28 + handle_errors(resp)
  29 + parser = ComFndParser.new(resp[0].data)
  30 + parser.parse
  31 + end
  32 +
  33 + def handle_errors(resp)
  34 + body = resp[0].http_body
  35 + raise SysproError, body if body =~ /^(ERROR)/
  36 + end
  37 + end
  38 + end
  39 +end
... ...
lib/syspro/business_objects/parsers/combrw_parser.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +module Syspro
  4 + module BusinessObjects
  5 + module Parsers
  6 + class ComBrwParser
  7 + attr_reader :doc
  8 +
  9 + def initialize(doc)
  10 + @doc = doc
  11 + end
  12 +
  13 + def parse
  14 + next_prev_key = doc.first_element_child.xpath('NextPrevKey')
  15 + next_prev_key_obj = next_prev_key.children.map do |el|
  16 + next if el.name == 'text'
  17 + {
  18 + name: el.name,
  19 + text: el.text
  20 + }
  21 + end.compact
  22 +
  23 + header_details = doc.first_element_child.xpath('HeaderDetails')
  24 + header_details_obj = header_details.children.map do |el|
  25 + next if el.name == 'text'
  26 + {
  27 + name: el.name,
  28 + text: el.text
  29 + }
  30 + end.compact
  31 +
  32 + rows = doc.first_element_child.xpath('Row')
  33 + rows_obj = rows.flat_map do |el|
  34 + el.elements.map do |inner|
  35 + {
  36 + name: inner.name,
  37 + value: inner.xpath('Value').text,
  38 + data_type: inner.xpath('DataType').text
  39 + }
  40 + end
  41 + end.compact
  42 +
  43 + BrowseObject.new(
  44 + doc.first_element_child.xpath('Title').text,
  45 + rows_obj,
  46 + next_prev_key_obj,
  47 + header_details_obj
  48 + )
  49 + end
  50 +
  51 + BrowseObject = Struct.new(:title, :rows, :next_prev_key, :header_details)
  52 + end
  53 + end
  54 + end
  55 +end
... ...
lib/syspro/business_objects/parsers/comfch_parser.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +module Syspro
  4 + module BusinessObjects
  5 + module Parsers
  6 + class ComFchParser
  7 + attr_reader :doc
  8 +
  9 + def initialize(doc)
  10 + @doc = doc
  11 + end
  12 +
  13 + def parse
  14 + table_name = doc.first_element_child.name
  15 + columns = doc.first_element_child.elements
  16 + columns_obj = columns.map do |el|
  17 + { name: el.name, value: el.children.text }
  18 + end.compact
  19 +
  20 + FetchObject.new(
  21 + table_name,
  22 + columns_obj
  23 + )
  24 + end
  25 +
  26 + FetchObject = Struct.new(:table_name, :columns)
  27 + end
  28 + end
  29 + end
  30 +end
... ...
lib/syspro/business_objects/parsers/comfnd_parser.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +module Syspro
  4 + module BusinessObjects
  5 + module Parsers
  6 + class ComFndParser
  7 + attr_reader :doc
  8 +
  9 + def initialize(doc)
  10 + @doc = doc
  11 + end
  12 +
  13 + def parse
  14 + header_details = doc.first_element_child.xpath('HeaderDetails')
  15 + header_details_obj = header_details.children.map do |el|
  16 + next if el.name == 'text'
  17 + {
  18 + name: el.name,
  19 + text: el.text
  20 + }
  21 + end.compact
  22 +
  23 + rows = doc.first_element_child.xpath('Row')
  24 + rows_obj = rows.flat_map do |el|
  25 + el.elements.map do |inner|
  26 + {
  27 + name: inner.name,
  28 + value: inner.children.text
  29 + }
  30 + end
  31 + end.compact
  32 +
  33 + QueryObject.new(
  34 + header_details_obj,
  35 + rows_obj,
  36 + doc.first_element_child.xpath('//RowsReturned').text.to_i
  37 + )
  38 + end
  39 +
  40 + QueryObject = Struct.new(:header_details, :rows, :row_count)
  41 + end
  42 + end
  43 + end
  44 +end
... ...
lib/syspro/business_objects/schemas/combrw.xml.erb 0 โ†’ 100644
  1 +<?xml version="1.0" encoding="Windows-1252"?>
  2 +<Browse xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xsd:noNamespaceSchemaLocation="COMBRW.XSD">
  3 + <BrowseName><%= @browse_name %></BrowseName>
  4 + <StartAtKey/>
  5 + <StartCondition><%= @start_condition %></StartCondition>
  6 + <ReturnRows><%= @return_rows %></ReturnRows>
  7 + <% for @filter in @filters %>
  8 + <Filter>
  9 + <ColumnFilterName><%= @filter[:name] %></ColumnFilterName>
  10 + <ColumnFilterValue><%= @filter[:value] %></ColumnFilterValue>
  11 + </Filter>
  12 + <% end %>
  13 + <BrowseDetails>
  14 + <TableName><%= @table_name %></TableName>
  15 + <Title><%= @title %></Title>
  16 + <% for @column in @columns %>
  17 + <Column>
  18 + <% unless @column[:name].nil? %><ColumnName><%= @column[:name] %></ColumnName><% end %>
  19 + <% unless @column[:description].nil? %><ColumnDescription><%= @column[:description] %></ColumnDescription><% end %>
  20 + <% unless @column[:key].nil? %><ColumnKey><%= @column[:key] %></ColumnKey><% end %>
  21 + </Column>
  22 + <% end %>
  23 + </BrowseDetails>
  24 +</Browse>
  25 +
... ...
lib/syspro/business_objects/schemas/comfch.xml.erb 0 โ†’ 100644
  1 +<?xml version="1.0" encoding="Windows-1252"?>
  2 +<Fetch xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xsd:noNamespaceSchemaLocation="COMFCH.XSD">
  3 + <TableName><%= @table_name %></TableName>
  4 + <Key><%= @key %></Key>
  5 + <% unless @optional_keys.empty? %>
  6 + <% @optional_keys.each_with_index do |key, i| %>
  7 + <<%= "OptionalKey#{ i + 1 }" %>><%= key[:value] %></<%= "OptionalKey#{ i + 1 }" %>>
  8 + <% end %>
  9 + <% end %>
  10 + <FullKeyProvided><%= @full_key_provided ? "N" : "Y" %></FullKeyProvided>
  11 + <DefaultType><%= @default_type %></DefaultType>
  12 + <EspressoFetch><%= @espresso_fetch ? "N" : "Y" %></EspressoFetch>
  13 +</Fetch>
  14 +
... ...
lib/syspro/business_objects/schemas/comfnd.xml.erb 0 โ†’ 100644
  1 +<?xml version="1.0" encoding="Windows-1252"?>
  2 +<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xsd:noNamespaceSchemaLocation="COMFND.XSD">
  3 + <TableName><%= @table_name %></TableName>
  4 + <ReturnRows><%= @return_rows %></ReturnRows>
  5 + <Columns>
  6 + <% for column in @columns %>
  7 + <Column><%= column[:name] %></Column>
  8 + <% end %>
  9 + </Columns>
  10 + <% unless @expressions.empty? %>
  11 + <Where>
  12 + <% for expression in @expressions %>
  13 + <Expression>
  14 + <OpenBracket>(</OpenBracket>
  15 + <% unless expression[:andor].nil? %>
  16 + <AndOr><%= expression[:andor] %></AndOr>
  17 + <% end %>
  18 + <Column><%= expression[:column] %></Column>
  19 + <Condition><%= expression[:condition] %></Condition>
  20 + <Value><%= expression[:value] %></Value>
  21 + <CloseBracket>)</CloseBracket>
  22 + </Expression>
  23 + <% end %>
  24 + </Where>
  25 + <% end %>
  26 + <OrderBy>
  27 + <Column><%= @order_by %></Column>
  28 + </OrderBy>
  29 +</Query>
... ...
lib/syspro/errors.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2 4 class SysproError < StandardError
3 5 attr_reader :message, :response, :code, :http_body, :http_headers,
... ... @@ -16,8 +18,8 @@ module Syspro
16 18 end
17 19  
18 20 def to_s
19   - status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
20   - id_string = @request_id.nil? ? "" : "(Request #{@request_id}) "
  21 + status_string = @http_status.nil? ? '' : "(Status #{@http_status}) "
  22 + id_string = @request_id.nil? ? '' : "(Request #{@request_id}) "
21 23 "#{status_string}#{id_string}#{@message}"
22 24 end
23 25 end
... ... @@ -30,5 +32,4 @@ module Syspro
30 32  
31 33 class ApiError < SysproError
32 34 end
33   -
34 35 end
... ...
lib/syspro/get_logon_profile.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2 4 class GetLogonProfile < ApiResource
3 5 def self.get_logon_profile(user_id)
4   - params = { "UserId" => user_id }
5   - resp = self.request(:get, resource_url, params)
  6 + params = { 'UserId' => user_id }
  7 + resp = request(:get, resource_url, params)
6 8 parse_response(resp[0])
7 9 end
8 10  
9 11 def resource_url
10   - "/GetLogonProfile"
  12 + '/GetLogonProfile'
11 13 end
12 14  
13   - def self.parse_response(resp)
  15 + def self.parse_response(resp) # rubocop:disable Metrics/MethodLength
14 16 doc = resp.data
15 17  
16   - UserProfile.new(
17   - doc.xpath("//CompanyName").text,
18   - doc.xpath("//OperatorCode").text,
19   - doc.xpath("//OperatorGroup").text,
20   - doc.xpath("//OperatorEmailAddress").text,
21   - doc.xpath("//OperatorLocation").text,
22   - doc.xpath("//OperatorLanguageCode").text,
23   - doc.xpath("//SystemLanguage").text,
24   - doc.xpath("//AccountingDate").text,
25   - doc.xpath("//CompanyDate").text,
26   - doc.xpath("//DefaultArBranch").text,
27   - doc.xpath("//DefaultApBranch").text,
28   - doc.xpath("//DefaultBank").text,
29   - doc.xpath("//DefaultWarehouse").text,
30   - doc.xpath("//DefaultCustomer").text,
31   - doc.xpath("//SystemSiteId").text,
32   - doc.xpath("//SystemNationalityCode").text,
33   - doc.xpath("//LocalCurrencyCode").text,
34   - doc.xpath("//CurrencyDescription").text,
35   - doc.xpath("//DefaultRequisitionUser").text,
36   - doc.xpath("//XMLToHTMLTransform").text,
37   - doc.xpath("//CssStyle").text,
38   - doc.xpath("//CssSuffix").text,
39   - doc.xpath("//DecimalFormat").text,
40   - doc.xpath("//DateFormat").text,
41   - doc.xpath("//FunctionalRole").text,
42   - doc.xpath("//DatabaseType").text,
43   - doc.xpath("//SysproVersion").text,
44   - doc.xpath("//EnetVersion").text,
45   - doc.xpath("//SysproServerBitWidth").text,
  18 + OpenStruct.new(
  19 + company_name: doc.xpath('//CompanyName').text,
  20 + operator_code: doc.xpath('//OperatorCode').text,
  21 + operator_code: doc.xpath('//OperatorGroup').text,
  22 + operator_email_address: doc.xpath('//OperatorEmailAddress').text,
  23 + operator_location: doc.xpath('//OperatorLocation').text,
  24 + operator_language_code: doc.xpath('//OperatorLanguageCode').text,
  25 + system_language: doc.xpath('//SystemLanguage').text,
  26 + accounting_date: doc.xpath('//AccountingDate').text,
  27 + company_date: doc.xpath('//CompanyDate').text,
  28 + default_ar_branch: doc.xpath('//DefaultArBranch').text,
  29 + default_ap_branch: doc.xpath('//DefaultApBranch').text,
  30 + default_bank: doc.xpath('//DefaultBank').text,
  31 + default_warehouse: doc.xpath('//DefaultWarehouse').text,
  32 + default_customer: doc.xpath('//DefaultCustomer').text,
  33 + system_site_id: doc.xpath('//SystemSiteId').text,
  34 + system_nationality_code: doc.xpath('//SystemNationalityCode').text,
  35 + local_currency_code: doc.xpath('//LocalCurrencyCode').text,
  36 + currency_description: doc.xpath('//CurrencyDescription').text,
  37 + default_requisition_user: doc.xpath('//DefaultRequisitionUser').text,
  38 + xml_to_html_transform: doc.xpath('//XMLToHTMLTransform').text,
  39 + css_style: doc.xpath('//CssStyle').text,
  40 + css_suffix: doc.xpath('//CssSuffix').text,
  41 + decimal_format: doc.xpath('//DecimalFormat').text,
  42 + date_format: doc.xpath('//DateFormat').text,
  43 + functional_role: doc.xpath('//FunctionalRole').text,
  44 + database_type: doc.xpath('//DatabaseType').text,
  45 + syspro_version: doc.xpath('//SysproVersion').text,
  46 + enet_version: doc.xpath('//EnetVersion').text,
  47 + syspro_server_bit_width: doc.xpath('//SysproServerBitWidth').text
46 48 )
47 49 end
48 50 private_class_method :parse_response
49   -
50   - UserProfile = Struct.new(:company_name, :operator_code, :operator_group, :operator_email_address,
51   - :operator_location, :operator_language_code, :system_language, :accounting_date,
52   - :company_date, :default_ar_branch, :default_ap_branch, :default_bank, :default_warehouse,
53   - :default_customer, :system_site_id, :system_nationality_code, :local_currency_code,
54   - :currency_description, :default_requisition_user, :xml_to_html_transform, :css_style,
55   - :css_suffix, :decimal_format, :date_format, :functional_role, :database_type, :syspro_version,
56   - :enet_version, :syspro_server_bit_width)
57 51 end
58 52 end
59   -
... ...
lib/syspro/get_version.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2 4 class GetVersion < ApiResource
3 5 def self.get_version
4   - resp = self.request(:get, resource_url)
  6 + resp = request(:get, resource_url)
5 7 VersionObject.new(resp[0].http_body)
6 8 end
7 9  
8 10 def resource_url
9   - "/GetVersion"
  11 + '/GetVersion'
10 12 end
11 13  
12 14 VersionObject = Struct.new(:version)
13 15 end
14 16 end
15   -
... ...
lib/syspro/logoff.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2 4 class Logoff < ApiResource
3 5 def self.logoff(user_id)
4   - params = { "UserId" => user_id }
5   - resp = self.request(:get, resource_url, params)
  6 + params = { 'UserId' => user_id }
  7 + resp = request(:get, resource_url, params)
6 8  
7   - if resp[0].http_body == "0"
  9 + if resp[0].http_body == '0'
8 10 true
9 11 else
10 12 resp[0].http_body
... ... @@ -12,7 +14,7 @@ module Syspro
12 14 end
13 15  
14 16 def resource_url
15   - "/Logoff"
  17 + '/Logoff'
16 18 end
17 19 end
18 20 end
... ...
lib/syspro/logon.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2 4 class Logon < ApiResource
3 5 def self.logon(username, password, company_id, company_password = nil)
4 6 params = {
5   - "Operator" => username,
6   - "OperatorPassword" => password,
7   - "CompanyId" => company_id,
8   - "CompanyPassword" => company_password
  7 + 'Operator' => username,
  8 + 'OperatorPassword' => password,
  9 + 'CompanyId' => company_id,
  10 + 'CompanyPassword' => company_password
9 11 }
10   - resp = self.request(:get, resource_url, params)
  12 + resp = request(:get, resource_url, params)
11 13 UserIdObject.new(resp[0].http_body)
12 14 end
13 15  
14 16 def resource_url
15   - "/Logon"
  17 + '/Logon'
16 18 end
17 19  
18 20 UserIdObject = Struct.new(:guid)
19 21 end
20 22 end
21   -
... ...
lib/syspro/singleton_api_resource.rb
1   -require_relative "api_resource"
  1 +# frozen_string_literal: true
  2 +
  3 +require_relative 'api_resource'
2 4  
3 5 module Syspro
4 6 class SingletonAPIResource < ApiResource
5 7 def self.resource_url
6 8 if self == SingletonAPIResource
7   - raise NotImplementedError, "SingletonAPIResource is an abstract class. You should perform actions on its subclasses (Customer, etc.)"
  9 + raise(
  10 + NotImplementedError,
  11 + 'SingletonAPIResource is an abstract class. You should perform actions on its subclasses (Customer, etc.)' # rubocop:disable Metrics/LineLength
  12 + )
8 13 end
9 14 "/#{CGI.escape(class_name.downcase)}"
10 15 end
... ...
lib/syspro/syspro_client.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2   - class SysproClient
  4 + # This class is the main syspro client
  5 + class SysproClient # rubocop:disable Metrics/ClassLength
3 6 attr_accessor :conn, :api_base
4 7  
5 8 @verify_ssl_warned = false
... ... @@ -13,7 +16,7 @@ module Syspro
13 16 Syspro::Logon.logon(username, password, company_id, company_password)
14 17 end
15 18  
16   - def get_syspro_version
  19 + def get_syspro_version # rubocop:disable Naming/AccessorMethodName
17 20 Syspro::GetVersion.get_version
18 21 end
19 22  
... ... @@ -22,13 +25,13 @@ module Syspro
22 25 end
23 26  
24 27 def self.default_client
25   - Thread.current[:syspro_client_default_client] ||= SysproClient.new(default_conn)
  28 + Thread.current[:syspro_client_default_client] ||= SysproClient.new(default_conn) # rubocop:disable Metrics/LineLength
26 29 end
27 30  
28 31 # A default Faraday connection to be used when one isn't configured. This
29 32 # object should never be mutated, and instead instantiating your own
30 33 # connection and wrapping it in a SysproClient object should be preferred.
31   - def self.default_conn
  34 + def self.default_conn # rubocop:disable Metrics/MethodLength
32 35 # We're going to keep connections around so that we can take advantage
33 36 # of connection re-use, so make sure that we have a separate connection
34 37 # object per thread.
... ... @@ -40,20 +43,21 @@ module Syspro
40 43 c.adapter Faraday.default_adapter
41 44 end
42 45  
43   - # For now, we're not verifying SSL certificates. The warning will appear.
44   - #if Syspro.verify_ssl_certs
45   - #conn.ssl.verify = true
46   - #conn.ssl.cert_store = Syspro.ca_store
47   - #else
  46 + # For now, we're not verifying SSL certificates.
  47 + # The warning will appear.
  48 + # if Syspro.verify_ssl_certs
  49 + # conn.ssl.verify = true
  50 + # conn.ssl.cert_store = Syspro.ca_store
  51 + # else
48 52 conn.ssl.verify = false
49 53  
50 54 unless @verify_ssl_warned
51 55 @verify_ssl_warned = true
52   - $stderr.puts("WARNING: Running without SSL cert verification. " \
53   - "You should never do this in production. " \
  56 + warn('WARNING: Running without SSL cert verification. ' \
  57 + 'You should never do this in production. ' \
54 58 "Execute 'Syspro.verify_ssl_certs = true' to enable verification.")
55 59 end
56   - #end
  60 + # end
57 61  
58 62 conn
59 63 end
... ... @@ -77,9 +81,9 @@ module Syspro
77 81 end
78 82 end
79 83  
80   - def execute_request(method, path, user_id: nil, api_base: nil, headers: {}, params: {})
  84 + def execute_request(method, path, user_id: nil, api_base: nil, headers: {}, params: {}) # rubocop:disable Metrics/LineLength, Metrics/MethodLength
81 85 api_base ||= Syspro.api_base
82   - user_id ||= ""
  86 + user_id ||= ''
83 87  
84 88 params = Util.objects_to_ids(params)
85 89 url = api_url(path, api_base)
... ... @@ -91,7 +95,7 @@ module Syspro
91 95 when :get, :head, :delete
92 96 query_params = params
93 97 else
94   - body = if headers[:content_type] && headers[:content_type] == "multipart/form-data"
  98 + body = if headers[:content_type] && headers[:content_type] == 'multipart/form-data' # rubocop:disable Metrics/LineLength
95 99 params
96 100 else
97 101 Util.encode_parameters(params)
... ... @@ -108,7 +112,7 @@ module Syspro
108 112 context.method = method
109 113 context.path = path
110 114 context.user_id = user_id
111   - context.query_params = query_params ? Util.encode_parameters(query_params) : nil
  115 + context.query_params = query_params ? Util.encode_parameters(query_params) : nil # rubocop:disable Metrics/LineLength
112 116  
113 117 http_resp = execute_request_with_rescues(api_base, context) do
114 118 conn.run_request(method, url, body, headers) do |req|
... ... @@ -135,22 +139,22 @@ module Syspro
135 139 http_status: status, http_body: body)
136 140 end
137 141  
138   - def api_url(url = "", api_base = nil)
  142 + def api_url(url = '', api_base = nil)
139 143 (api_base || Syspro.api_base) + url
140 144 end
141 145  
142   - def request_headers(method)
  146 + def request_headers(_method)
143 147 user_agent = "Syspro/7 RubyBindings/#{Syspro::VERSION}"
144 148  
145 149 headers = {
146   - "User-Agent" => user_agent,
147   - "Content-Type" => "application/x-www-form-urlencoded",
  150 + 'User-Agent' => user_agent,
  151 + 'Content-Type' => 'application/x-www-form-urlencoded'
148 152 }
149 153  
150 154 headers
151 155 end
152 156  
153   - def execute_request_with_rescues(api_base, context)
  157 + def execute_request_with_rescues(api_base, context) # rubocop:disable Metrics/LineLength, Metrics/MethodLength
154 158 num_retries = 0
155 159 begin
156 160 request_start = Time.now
... ... @@ -159,8 +163,8 @@ module Syspro
159 163 log_response(context, request_start, resp.status, resp.body)
160 164  
161 165 # We rescue all exceptions from a request so that we have an easy spot to
162   - # implement our retry logic across the board. We'll re-raise if it's a type
163   - # of exception that we didn't expect to handle.
  166 + # implement our retry logic across the board. We'll re-raise if it's a
  167 + # type of exception that we didn't expect to handle.
164 168 rescue StandardError => e
165 169 if e.respond_to?(:response) && e.response
166 170 log_response(context, request_start,
... ... @@ -178,9 +182,9 @@ module Syspro
178 182 case e
179 183 when Faraday::ClientError
180 184 if e.response
181   - handle_error_response(e.response, error_context)
  185 + handle_error_response(e.response, context)
182 186 else
183   - handle_network_error(e, error_context, num_retries, api_base)
  187 + handle_network_error(e, context, num_retries, api_base)
184 188 end
185 189  
186 190 # Only handle errors when we know we can do so, and re-raise otherwise.
... ... @@ -193,7 +197,36 @@ module Syspro
193 197 resp
194 198 end
195 199  
196   - def self.should_retry?(e, num_retries)
  200 + def handle_network_error(e, context, num_retries, api_base = nil) # rubocop:disable Metrics/LineLength, Metrics/MethodLength, Naming/UncommunicativeMethodParamName
  201 + Util.log_error('Syspro network error',
  202 + error_message: e.message,
  203 + request_id: context.request_id)
  204 +
  205 + case e
  206 + when Faraday::ConnectionFailed
  207 + message = 'Unexpected error communicating when trying to connect to Syspro.' # rubocop:disable Metrics/LineLength
  208 +
  209 + when Faraday::SSLError
  210 + message = 'Could not establish a secure connection to Syspro.'
  211 +
  212 + when Faraday::TimeoutError
  213 + api_base ||= Syspro.api_base
  214 + message = "Could not connect to Syspro (#{api_base}). " \
  215 + 'Please check your internet connection and try again. ' \
  216 + 'If this problem persists, you should check your Syspro service status.' # rubocop:disable Metrics/LineLength
  217 +
  218 + else
  219 + message = 'Unexpected error communicating with Syspro. ' \
  220 + 'If this problem persists, talk to your Syspro implementation team.'
  221 +
  222 + end
  223 +
  224 + message += " Request was retried #{num_retries} times." if num_retries.positive? # rubocop:disable Metrics/LineLength
  225 +
  226 + raise ApiConnectionError, message + "\n\n(Network error: #{e.message})"
  227 + end
  228 +
  229 + def self.should_retry?(e, num_retries) # rubocop:disable Metrics/LineLength, Naming/UncommunicativeMethodParamName
197 230 return false if num_retries >= Syspro.max_network_retries
198 231  
199 232 # Retry on timeout-related problems (either on open or read).
... ... @@ -213,20 +246,20 @@ module Syspro
213 246 end
214 247  
215 248 def log_request(context, num_retries)
216   - Util.log_info("Request to Syspro API",
  249 + Util.log_info('Request to Syspro API',
217 250 account: context.account,
218 251 api_version: context.api_version,
219 252 method: context.method,
220 253 num_retries: num_retries,
221 254 path: context.path)
222   - Util.log_debug("Request details",
  255 + Util.log_debug('Request details',
223 256 body: context.body,
224 257 query_params: context.query_params)
225 258 end
226 259 private :log_request
227 260  
228   - def log_response(context, request_start, status, body)
229   - Util.log_info("Response from Syspro API",
  261 + def log_response(context, request_start, status, body) # rubocop:disable Metrics/LineLength, Metrics/MethodLength
  262 + Util.log_info('Response from Syspro API',
230 263 account: context.account,
231 264 api_version: context.api_version,
232 265 elapsed: Time.now - request_start,
... ... @@ -234,14 +267,14 @@ module Syspro
234 267 path: context.path,
235 268 request_id: context.request_id,
236 269 status: status)
237   - Util.log_debug("Response details",
  270 + Util.log_debug('Response details',
238 271 body: body,
239 272 request_id: context.request_id)
240 273 end
241 274 private :log_response
242 275  
243   - def log_response_error(context, request_start, e)
244   - Util.log_error("Request error",
  276 + def log_response_error(context, request_start, e) # rubocop:disable Metrics/LineLength, Naming/UncommunicativeMethodParamName
  277 + Util.log_error('Request error',
245 278 elapsed: Time.now - request_start,
246 279 error_message: e.message,
247 280 method: context.method,
... ... @@ -249,6 +282,26 @@ module Syspro
249 282 end
250 283 private :log_response_error
251 284  
  285 + def handle_error_response(http_resp, context) # rubocop:disable Metrics/LineLength, Metrics/MethodLength
  286 + begin
  287 + resp = SysproResponse.from_faraday_hash(http_resp)
  288 + error_data = resp.data[:error]
  289 +
  290 + raise SysproError, 'Indeterminate error' unless error_data
  291 + rescue Nokogiri::XML::SyntaxError, SysproError
  292 + raise general_api_error(http_resp[:status], http_resp[:body])
  293 + end
  294 +
  295 + error = if error_data.is_a?(String)
  296 + specific_oauth_error(resp, error_data, context)
  297 + else
  298 + specific_api_error(resp, error_data, context)
  299 + end
  300 +
  301 + error.response = resp
  302 + raise(error)
  303 + end
  304 +
252 305 # RequestLogContext stores information about a request that's begin made so
253 306 # that we can log certain information. It's useful because it means that we
254 307 # don't have to pass around as many parameters.
... ... @@ -267,56 +320,55 @@ module Syspro
267 320 # in so that we can generate a rich user agent header to help debug
268 321 # integrations.
269 322 class SystemProfiler
270   - def self.uname
271   - if File.exist?("/proc/version")
272   - File.read("/proc/version").strip
  323 + def self.uname # rubocop:disable Metrics/MethodLength
  324 + if File.exist?('/proc/version')
  325 + File.read('/proc/version').strip
273 326 else
274   - case RbConfig::CONFIG["host_os"]
  327 + case RbConfig::CONFIG['host_os']
275 328 when /linux|darwin|bsd|sunos|solaris|cygwin/i
276 329 uname_from_system
277 330 when /mswin|mingw/i
278 331 uname_from_system_ver
279 332 else
280   - "unknown platform"
  333 + 'unknown platform'
281 334 end
282 335 end
283 336 end
284 337  
285 338 def self.uname_from_system
286   - (`uname -a 2>/dev/null` || "").strip
  339 + (`uname -a 2>/dev/null` || '').strip
287 340 rescue Errno::ENOENT
288   - "uname executable not found"
  341 + 'uname executable not found'
289 342 rescue Errno::ENOMEM # couldn't create subprocess
290   - "uname lookup failed"
  343 + 'uname lookup failed'
291 344 end
292 345  
293 346 def self.uname_from_system_ver
294   - (`ver` || "").strip
  347 + (`ver` || '').strip
295 348 rescue Errno::ENOENT
296   - "ver executable not found"
  349 + 'ver executable not found'
297 350 rescue Errno::ENOMEM # couldn't create subprocess
298   - "uname lookup failed"
  351 + 'uname lookup failed'
299 352 end
300 353  
301 354 def initialize
302 355 @uname = self.class.uname
303 356 end
304 357  
305   - def user_agent
306   - lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
  358 + def user_agent # rubocop:disable Metrics/MethodLength
  359 + lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})" # rubocop:disable Metrics/LineLength
307 360  
308 361 {
309 362 application: Syspro.app_info,
310 363 bindings_version: Syspro::VERSION,
311   - lang: "ruby",
  364 + lang: 'ruby',
312 365 lang_version: lang_version,
313 366 platform: RUBY_PLATFORM,
314   - engine: defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
  367 + engine: defined?(RUBY_ENGINE) ? RUBY_ENGINE : '',
315 368 uname: @uname,
316   - hostname: Socket.gethostname,
  369 + hostname: Socket.gethostname
317 370 }.delete_if { |_k, v| v.nil? }
318 371 end
319 372 end
320 373 end
321 374 end
322   -
... ...
lib/syspro/syspro_object.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
  4 + # This class represents a syspro response
2 5 class SysproObject
3 6 include Enumerable
4 7  
... ... @@ -18,7 +21,8 @@ module Syspro
18 21 # considered to be equal if they have the same set of values and each one
19 22 # of those values is the same.
20 23 def ==(other)
21   - other.is_a?(SysproObject) && @values == other.instance_variable_get(:@values)
  24 + other.is_a?(SysproObject) &&
  25 + @values == other.instance_variable_get(:@values)
22 26 end
23 27  
24 28 def to_s(*_args)
... ... @@ -26,8 +30,8 @@ module Syspro
26 30 end
27 31  
28 32 def inspect
29   - id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ""
30   - "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
  33 + id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ''
  34 + "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values) # rubocop:disable Metrics/LineLength
31 35 end
32 36  
33 37 def keys
... ... @@ -38,9 +42,9 @@ module Syspro
38 42 @values.values
39 43 end
40 44  
41   - def to_hash
  45 + def to_hash # rubocop:disable Metrics/MethodLength
42 46 maybe_to_hash = lambda do |value|
43   - value && value.respond_to?(:to_hash) ? value.to_hash : value
  47 + value&.respond_to?(:to_hash) ? value.to_hash : value
44 48 end
45 49  
46 50 @values.each_with_object({}) do |(key, value), acc|
... ... @@ -57,11 +61,9 @@ module Syspro
57 61 @values.each(&blk)
58 62 end
59 63  
60   - private
61   -
62 64 # Produces a deep copy of the given object including support for arrays,
63 65 # hashes, and SysproObjects.
64   - def self.deep_copy(obj)
  66 + def self.deep_copy(obj) # rubocop:disable Metrics/MethodLength
65 67 case obj
66 68 when Array
67 69 obj.map { |e| deep_copy(e) }
... ... @@ -82,6 +84,5 @@ module Syspro
82 84 end
83 85 end
84 86 private_class_method :deep_copy
85   -
86 87 end
87 88 end
... ...
lib/syspro/syspro_response.rb
1   -require "nokogiri"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'nokogiri'
2 4  
3 5 module Syspro
  6 + # This class represents a syspro response
4 7 class SysproResponse
5 8 attr_accessor :data, :http_body, :http_headers, :http_status, :request_id
6 9  
... ... @@ -12,7 +15,7 @@ module Syspro
12 15 resp.data = Nokogiri::XML(resp.http_body)
13 16 resp.http_headers = http_resp[:headers]
14 17 resp.http_status = http_resp[:status]
15   - resp.request_id = http_resp[:headers]["Request-Id"]
  18 + resp.request_id = http_resp[:headers]['Request-Id']
16 19 resp
17 20 end
18 21  
... ... @@ -23,7 +26,7 @@ module Syspro
23 26 resp.data = Nokogiri::XML(resp.http_body)
24 27 resp.http_headers = http_resp.headers
25 28 resp.http_status = http_resp.status
26   - resp.request_id = http_resp.headers["Request-Id"]
  29 + resp.request_id = http_resp.headers['Request-Id']
27 30 resp
28 31 end
29 32 end
... ...
lib/syspro/util.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2   - class Util
  4 + # Utillity class
  5 + class Util # rubocop:disable Metrics/ClassLength
3 6 # Options that a user is allowed to specify.
4 7 OPTS_USER_SPECIFIED = Set[
5 8 :user_id
... ... @@ -17,8 +20,7 @@ module Syspro
17 20 OPTS_USER_SPECIFIED + Set[:client]
18 21 ).freeze
19 22  
20   -
21   - def self.objects_to_ids(h)
  23 + def self.objects_to_ids(h) # rubocop:disable Metrics/MethodLength, Metrics/LineLength, Naming/UncommunicativeMethodParamName
22 24 case h
23 25 when ApiResource
24 26 h.id
... ... @@ -45,13 +47,17 @@ module Syspro
45 47 # * +data+ - Hash of fields and values to be converted into a SysproObject.
46 48 # * +opts+ - Options for +SysproObject+ like an API key that will be reused
47 49 # on subsequent API calls.
48   - def self.convert_to_syspro_object(data, opts = {})
  50 + def self.convert_to_syspro_object(data, opts = {}) # rubocop:disable Metrics/LineLength, Metrics/MethodLength
49 51 case data
50 52 when Array
51 53 data.map { |i| convert_to_syspro_object(i, opts) }
52 54 when Hash
53   - # Try converting to a known object class. If none available, fall back to generic SysproObject
54   - object_classes.fetch(data[:object], SysproObject).construct_from(data, opts)
  55 + # Try converting to a known object class.
  56 + # If none available, fall back to generic SysproObject
  57 + object_classes.fetch(
  58 + data[:object],
  59 + SysproObject
  60 + ).construct_from(data, opts)
55 61 else
56 62 data
57 63 end
... ... @@ -66,7 +72,7 @@ module Syspro
66 72 when Hash
67 73 opts.clone
68 74 else
69   - raise TypeError, "normalize_opts expects a string or a hash"
  75 + raise TypeError, 'normalize_opts expects a string or a hash'
70 76 end
71 77 end
72 78  
... ... @@ -78,7 +84,7 @@ module Syspro
78 84 def self.normalize_headers(headers)
79 85 headers.each_with_object({}) do |(k, v), new_headers|
80 86 if k.is_a?(Symbol)
81   - k = titlecase_parts(k.to_s.tr("_", "-"))
  87 + k = titlecase_parts(k.to_s.tr('_', '-'))
82 88 elsif k.is_a?(String)
83 89 k = titlecase_parts(k)
84 90 end
... ... @@ -89,10 +95,10 @@ module Syspro
89 95  
90 96 def self.encode_parameters(params)
91 97 Util.flatten_params(params)
92   - .map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join("&")
  98 + .map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join('&')
93 99 end
94 100  
95   - def self.flatten_params(params, parent_key = nil)
  101 + def self.flatten_params(params, parent_key = nil) # rubocop:disable Metrics/LineLength, Metrics/MethodLength
96 102 result = []
97 103  
98 104 # do not sort the final output because arrays (and arrays of hashes
... ... @@ -113,26 +119,41 @@ module Syspro
113 119 end
114 120  
115 121 def self.log_error(message, data = {})
116   - if !Syspro.logger.nil? ||
117   - !Syspro.log_level.nil? && Syspro.log_level <= Syspro::LEVEL_ERROR
118   - log_internal(message, data, color: :cyan,
119   - level: Syspro::LEVEL_ERROR, logger: Syspro.logger, out: $stderr)
  122 + if !Syspro.logger.nil? || !Syspro.log_level.nil? && Syspro.log_level <= Syspro::LEVEL_ERROR # rubocop:disable Style/GuardClause, Metrics/LineLength
  123 + log_internal(
  124 + message,
  125 + data,
  126 + color: :cyan,
  127 + level: Syspro::LEVEL_ERROR,
  128 + logger: Syspro.logger,
  129 + out: $stderr
  130 + )
120 131 end
121 132 end
122 133  
123 134 def self.log_info(message, data = {})
124   - if !Syspro.logger.nil? ||
125   - !Syspro.log_level.nil? && Syspro.log_level <= Syspro::LEVEL_INFO
126   - log_internal(message, data, color: :cyan,
127   - level: Syspro::LEVEL_INFO, logger: Syspro.logger, out: $stdout)
  135 + if !Syspro.logger.nil? || !Syspro.log_level.nil? && Syspro.log_level <= Syspro::LEVEL_INFO # rubocop:disable Style/GuardClause, Metrics/LineLength
  136 + log_internal(
  137 + message,
  138 + data,
  139 + color: :cyan,
  140 + level: Syspro::LEVEL_INFO,
  141 + logger: Syspro.logger,
  142 + out: $stdout
  143 + )
128 144 end
129 145 end
130 146  
131 147 def self.log_debug(message, data = {})
132   - if !Syspro.logger.nil? ||
133   - !Syspro.log_level.nil? && Syspro.log_level <= Syspro::LEVEL_DEBUG
134   - log_internal(message, data, color: :blue,
135   - level: Syspro::LEVEL_DEBUG, logger: Syspro.logger, out: $stdout)
  148 + if !Syspro.logger.nil? || !Syspro.log_level.nil? && Syspro.log_level <= Syspro::LEVEL_DEBUG # rubocop:disable Style/GuardClause, Metrics/LineLength
  149 + log_internal(
  150 + message,
  151 + data,
  152 + color: :blue,
  153 + level: Syspro::LEVEL_DEBUG,
  154 + logger: Syspro.logger,
  155 + out: $stdout
  156 + )
136 157 end
137 158 end
138 159  
... ... @@ -141,8 +162,47 @@ module Syspro
141 162 # Don't use strict form encoding by changing the square bracket control
142 163 # characters back to their literals. This is fine by the server, and
143 164 # makes these parameter strings easier to read.
144   - gsub("%5B", "[").gsub("%5D", "]")
  165 + gsub('%5B', '[').gsub('%5D', ']')
145 166 end
  167 +
  168 + # TODO: Make these named required arguments when we drop support for Ruby
  169 + # 2.0.
  170 + def self.log_internal(message, data = {}, color: nil, level: nil, logger: nil, out: nil) # rubocop:disable Metrics/LineLength, Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists
  171 + data_str = data.reject { |_k, v| v.nil? }.map do |(k, v)|
  172 + format(
  173 + '%s=%s', # rubocop:disable Style/FormatStringToken
  174 + colorize(k, color, !out.nil? && out.isatty),
  175 + wrap_logfmt_value(v)
  176 + )
  177 + end.join(' ')
  178 +
  179 + if !logger.nil?
  180 + # the library's log levels are mapped to the same values as the
  181 + # standard library's logger
  182 + logger.log(
  183 + level,
  184 + format(
  185 + 'message=%s %s', # rubocop:disable Style/FormatStringToken
  186 + wrap_logfmt_value(message),
  187 + data_str
  188 + )
  189 + )
  190 + elsif out.isatty
  191 + out.puts format(
  192 + '%s %s %s', # rubocop:disable Style/FormatStringToken
  193 + colorize(level_name(level)[0, 4].upcase, color, out.isatty),
  194 + message,
  195 + data_str
  196 + )
  197 + else
  198 + out.puts format(
  199 + 'message=%s level=%s %s', # rubocop:disable Style/FormatStringToken
  200 + wrap_logfmt_value(message),
  201 + level_name(level),
  202 + data_str
  203 + )
  204 + end
  205 + end
  206 + private_class_method :log_internal
146 207 end
147 208 end
148   -
... ...
lib/syspro/version.rb
  1 +# frozen_string_literal: true
  2 +
1 3 module Syspro
2   - VERSION = "0.1.0"
  4 + VERSION = '1.0.0.alpha.1'
3 5 end
... ...
syspro-ruby.gemspec
1   -lib = File.expand_path("../lib", __FILE__)
  1 +# frozen_string_literal: true
  2 +
  3 +lib = File.expand_path('lib', __dir__)
2 4 $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3   -require "syspro/version"
  5 +require 'syspro/version'
4 6  
5 7 Gem::Specification.new do |spec|
6   - spec.name = "syspro-ruby"
  8 + spec.name = 'syspro-ruby'
7 9 spec.version = Syspro::VERSION
8   - spec.authors = ["Isaac Lewis"]
9   - spec.email = ["isaac@ike.io"]
  10 + spec.authors = ['Isaac Lewis']
  11 + spec.email = ['isaac@ike.io']
10 12  
11   - spec.summary = %q{SYSPRO 7 Api Ruby adapter}
12   - spec.license = "MIT"
  13 + spec.summary = 'SYSPRO 7 Api Ruby adapter'
  14 + spec.license = 'MIT'
13 15  
14 16 # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
15 17 # to allow pushing to a single host or delete this section to allow pushing to any host.
16 18 if spec.respond_to?(:metadata)
17   - spec.metadata["allowed_push_host"] = "http://rubygems.org"
  19 + spec.metadata['allowed_push_host'] = 'http://rubygems.org'
18 20 else
19   - raise "RubyGems 2.0 or newer is required to protect against " \
20   - "public gem pushes."
  21 + raise 'RubyGems 2.0 or newer is required to protect against ' \
  22 + 'public gem pushes.'
21 23 end
  24 + spec.required_ruby_version = '>= 1.9.0'
22 25  
23   - spec.files = `git ls-files -z`.split("\x0").reject do |f|
  26 + spec.files = `git ls-files -z`.split("\x0").reject do |f|
24 27 f.match(%r{^(test|spec|features)/})
25 28 end
26   - spec.bindir = "exe"
  29 + spec.bindir = 'exe'
27 30 spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28   - spec.require_paths = ["lib"]
  31 + spec.require_paths = ['lib']
29 32  
30   - spec.add_dependency("faraday", "~> 0.10")
  33 + spec.add_dependency('faraday', '~> 0.10')
  34 + spec.add_dependency('nokogiri', '~> 1.8.2')
31 35  
32   - spec.add_development_dependency "bundler", "~> 1.16"
33   - spec.add_development_dependency "pry", "~> 0.11"
34   - spec.add_development_dependency "rake", "~> 10.0"
35   - spec.add_development_dependency "minitest", "~> 5.0"
  36 + spec.add_development_dependency 'bundler', '~> 1.16'
  37 + spec.add_development_dependency 'minitest', '~> 5.0'
  38 + spec.add_development_dependency 'pry', '~> 0.11'
  39 + spec.add_development_dependency 'rake', '~> 10.0'
  40 + spec.add_development_dependency 'rubocop', '~> 0.54.0'
  41 + spec.add_development_dependency 'webmock', '~> 3.3.0'
  42 + spec.add_development_dependency 'minitest-vcr', '~> 1.4.0'
36 43 end
... ...
test/cassettes/test_client_block_execution.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/getversion
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '7'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:23:43 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: 7.0.0.6
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:23:42 GMT
  36 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_get_syspro_version.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/getversion
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '7'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:23:43 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: 7.0.0.6
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:23:42 GMT
  36 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_logoff_error.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logoff?UserId=1BB5B3050954BB459A5D034DB5CC386980
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '72'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:22:53 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: 'ERROR: Unable to read the SYSPRO base directory registry string BaseDir8'
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:22:52 GMT
  36 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_logon.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logon?CompanyId=L&CompanyPassword=&Operator=wland&OperatorPassword=piperita2016
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '36'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:23:52 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: 'EC3098D6E284FB44ADF10671B3F06FCA00 '
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:23:51 GMT
  36 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_query_browse.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logon?CompanyId=L&CompanyPassword=&Operator=wland&OperatorPassword=piperita2016
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '36'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:23:03 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: '64C41E1DEB73024CA660211DE643705700 '
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:23:01 GMT
  36 +- request:
  37 + method: get
  38 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/Query/Browse?UserId=64C41E1DEB73024CA660211DE643705700%20%20&XmlIn=%3C?xml%20version=%221.0%22%20encoding=%22Windows-1252%22?%3E%0A%3CBrowse%20xmlns:xsd=%22http://www.w3.org/2001/XMLSchema-instance%22%20xsd:noNamespaceSchemaLocation=%22COMBRW.XSD%22%3E%0A%20%20%3CBrowseName%3EInvMaster%3C/BrowseName%3E%0A%20%20%3CStartAtKey/%3E%0A%20%20%3CStartCondition%3E%3C/StartCondition%3E%0A%20%20%3CReturnRows%3E5%3C/ReturnRows%3E%0A%20%20%0A%20%20%3CBrowseDetails%3E%0A%20%20%20%20%3CTableName%3EInvMaster%3C/TableName%3E%0A%20%20%20%20%3CTitle%3EStockCodes%3C/Title%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%3CColumn%3E%0A%20%20%20%20%20%20%20%20%3CColumnName%3EStockCode%3C/ColumnName%3E%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%3C/Column%3E%0A%20%20%20%20%0A%20%20%3C/BrowseDetails%3E%0A%3C/Browse%3E%0A%0A
  39 + body:
  40 + encoding: US-ASCII
  41 + string: ''
  42 + headers:
  43 + User-Agent:
  44 + - Syspro/7 RubyBindings/0.1.0
  45 + Content-Type:
  46 + - application/x-www-form-urlencoded
  47 + Accept-Encoding:
  48 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  49 + Accept:
  50 + - "*/*"
  51 + response:
  52 + status:
  53 + code: 200
  54 + message: OK
  55 + headers:
  56 + Content-Length:
  57 + - '944'
  58 + Content-Type:
  59 + - application/octet-stream
  60 + Server:
  61 + - Microsoft-HTTPAPI/2.0
  62 + Date:
  63 + - Fri, 06 Apr 2018 19:23:07 GMT
  64 + body:
  65 + encoding: UTF-8
  66 + string: "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n<InvMaster Language='05'
  67 + Language2='EN' CssStyle='' DecFormat='1' DateFormat='01' Role='01' Version='7.0.036'
  68 + OperatorPrimaryRole=' '>\n<Title>StockCodes</Title>\n<Row>\n<StockCode>\n<Value>02</Value>\n<DataType>AlphaNumeric</DataType>\n</StockCode>\n</Row>\n<Row>\n<StockCode>\n<Value>021</Value>\n<DataType>AlphaNumeric</DataType>\n</StockCode>\n</Row>\n<Row>\n<StockCode>\n<Value>0214011IFF</Value>\n<DataType>AlphaNumeric</DataType>\n</StockCode>\n</Row>\n<Row>\n<StockCode>\n<Value>022</Value>\n<DataType>AlphaNumeric</DataType>\n</StockCode>\n</Row>\n<Row>\n<StockCode>\n<Value>023</Value>\n<DataType>AlphaNumeric</DataType>\n</StockCode>\n</Row>\n<NextPrevKey>\n<PrevKey>02</PrevKey>\n<NextKey>023</NextKey>\n<Fwd>True</Fwd>\n<Back>False</Back>\n</NextPrevKey>\n<HeaderDetails>\n<Header>Stock
  69 + code</Header>\n<Key>StockCode</Key>\n<KeyDescription>Stock code</KeyDescription>\n<Table>InvMaster</Table>\n</HeaderDetails>\n</InvMaster>\n "
  70 + http_version:
  71 + recorded_at: Fri, 06 Apr 2018 19:23:06 GMT
  72 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_query_fetch.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logon?CompanyId=L&CompanyPassword=&Operator=wland&OperatorPassword=piperita2016
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '36'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:23:16 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: '5B21680F424424498B7CE7CCDA98B41700 '
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:23:15 GMT
  36 +- request:
  37 + method: get
  38 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/Query/Fetch?UserId=5B21680F424424498B7CE7CCDA98B41700%20%20&XmlIn=%3C?xml%20version=%221.0%22%20encoding=%22Windows-1252%22?%3E%0A%3CFetch%20xmlns:xsd=%22http://www.w3.org/2001/XMLSchema-instance%22%20xsd:noNamespaceSchemaLocation=%22COMFCH.XSD%22%3E%0A%20%20%3CTableName%3EInvMaster%3C/TableName%3E%0A%20%20%3CKey%3E02%3C/Key%3E%0A%20%20%0A%20%20%3CFullKeyProvided%3EY%3C/FullKeyProvided%3E%0A%20%20%3CDefaultType%3E%3C/DefaultType%3E%0A%20%20%3CEspressoFetch%3EN%3C/EspressoFetch%3E%0A%3C/Fetch%3E%0A%0A
  39 + body:
  40 + encoding: US-ASCII
  41 + string: ''
  42 + headers:
  43 + User-Agent:
  44 + - Syspro/7 RubyBindings/0.1.0
  45 + Content-Type:
  46 + - application/x-www-form-urlencoded
  47 + Accept-Encoding:
  48 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  49 + Accept:
  50 + - "*/*"
  51 + response:
  52 + status:
  53 + code: 200
  54 + message: OK
  55 + headers:
  56 + Content-Length:
  57 + - '3911'
  58 + Content-Type:
  59 + - application/octet-stream
  60 + Server:
  61 + - Microsoft-HTTPAPI/2.0
  62 + Date:
  63 + - Fri, 06 Apr 2018 19:23:26 GMT
  64 + body:
  65 + encoding: UTF-8
  66 + string: "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n<InvMaster Language='05'
  67 + Language2='EN' CssStyle='' DecFormat='1' DateFormat='01' Role='01' Version='7.0.035'
  68 + OperatorPrimaryRole=' '>\n<StockCode>02</StockCode>\n<Description>STR</Description>\n<LongDesc>SPEARMINT
  69 + TERPENES</LongDesc>\n<AlternateKey1/>\n<AlternateKey2/>\n<EccUser/>\n<StockUom>LB</StockUom>\n<AlternateUom>KG</AlternateUom>\n<OtherUom>DR</OtherUom>\n<ConvFactAltUom>
  70 + \ 2.204620</ConvFactAltUom>\n<ConvMulDiv>M</ConvMulDiv>\n<ConvFactOthUom>
  71 + \ 400.000000</ConvFactOthUom>\n<MulDiv>M</MulDiv>\n<Mass> 1.000000</Mass>\n<Volume>
  72 + \ 0.000000</Volume>\n<Decimals>3</Decimals>\n<PriceCategory>A</PriceCategory>\n<PriceMethod>C</PriceMethod>\n<Supplier/>\n<CycleCount>
  73 + 0</CycleCount>\n<ProductClass>STRP</ProductClass>\n<TaxCode>A</TaxCode>\n<OtherTaxCode/>\n<ListPriceCode>A</ListPriceCode>\n<SerialMethod>N</SerialMethod>\n<InterfaceFlag>Y</InterfaceFlag>\n<KitType>N</KitType>\n<LowLevelCode>
  74 + 0</LowLevelCode>\n<Buyer/>\n<Planner/>\n<TraceableType>T</TraceableType>\n<MpsFlag>N</MpsFlag>\n<BulkIssueFlag>N</BulkIssueFlag>\n<AbcClass/>\n<LeadTime>
  75 + \ 0</LeadTime>\n<StockMovementReq>Y</StockMovementReq>\n<ClearingFlag>N</ClearingFlag>\n<SupercessionDate>0000-00-00</SupercessionDate>\n<AbcAnalysisReq>Y</AbcAnalysisReq>\n<AbcCostingReq>N</AbcCostingReq>\n<CostUom>LB</CostUom>\n<MinPricePct>
  76 + \ 0.00</MinPricePct>\n<LabourCost> 0.00000</LabourCost>\n<MaterialCost>
  77 + \ 0.00000</MaterialCost>\n<FixOverhead> 0.00000</FixOverhead>\n<VariableOverhead>
  78 + \ 0.00000</VariableOverhead>\n<PartCategory>B</PartCategory>\n<DrawOfficeNum/>\n<WarehouseToUse>H1</WarehouseToUse>\n<BuyingRule>A</BuyingRule>\n<SpecificGravity>
  79 + 0.0000</SpecificGravity>\n<ImplosionNum> 0</ImplosionNum>\n<Ebq> 400.000000</Ebq>\n<ComponentCount>
  80 + \ 0</ComponentCount>\n<FixTimePeriod> 1</FixTimePeriod>\n<PanSize> 0.000000</PanSize>\n<DockToStock>
  81 + \ 0</DockToStock>\n<OutputMassFlag>F</OutputMassFlag>\n<ShelfLife>
  82 + \ 0</ShelfLife>\n<Version/>\n<Release/>\n<DemandTimeFence> 0</DemandTimeFence>\n<MakeToOrderFlag>N</MakeToOrderFlag>\n<ManufLeadTime>
  83 + \ 0</ManufLeadTime>\n<GrossReqRule>I</GrossReqRule>\n<PercentageYield>100</PercentageYield>\n<AbcPreProd>
  84 + \ 0.00000</AbcPreProd>\n<AbcManufacturing> 0.00000</AbcManufacturing>\n<AbcSales>
  85 + \ 0.00000</AbcSales>\n<AbcCumPreProd> 0.00000</AbcCumPreProd>\n<AbcCumManuf>
  86 + \ 0.00000</AbcCumManuf>\n<WipCtlGlCode/>\n<ResourceCode/>\n<GstTaxCode>A</GstTaxCode>\n<PrcInclGst>N</PrcInclGst>\n<SerEntryAtSale/>\n<StpSelection/>\n<UserField1/>\n<UserField2>
  87 + \ 0.00000</UserField2>\n<UserField3/>\n<UserField4/>\n<UserField5/>\n<TariffCode/>\n<SupplementaryUnit>N</SupplementaryUnit>\n<EbqPan>E</EbqPan>\n<StdLandedCost>
  88 + \ 0.00000</StdLandedCost>\n<LctRequired>N</LctRequired>\n<StdLctRoute/>\n<IssMultLotsFlag>Y</IssMultLotsFlag>\n<InclInStrValid>Y</InclInStrValid>\n<StdLabCostsBill>
  89 + \ 0.00000</StdLabCostsBill>\n<PhantomIfComp/>\n<CountryOfOrigin/>\n<StockOnHold/>\n<StockOnHoldReason/>\n<EccFlag>N</EccFlag>\n<StockAndAltUm>N</StockAndAltUm>\n<AltUnitChar>0</AltUnitChar>\n<JobsOnHold/>\n<JobHoldAllocs/>\n<PurchOnHold/>\n<SalesOnHold/>\n<MaintOnHold/>\n<BatchBill>N</BatchBill>\n<BlanketPoExists/>\n<CallOffBpoExists/>\n<DistWarehouseToUse/>\n<JobClassification/>\n<SubContractCost>
  90 + \ 0.00000</SubContractCost>\n<DateStkAdded>2005-08-26</DateStkAdded>\n<InspectionFlag/>\n<SerialPrefix/>\n<SerialSuffix/>\n<ReturnableItem/>\n<ProductGroup/>\n<PriceType/>\n<Basis/>\n<ManualCostFlag/>\n<ManufactureUom>LB</ManufactureUom>\n<ConvFactMuM>
  91 + \ 1.000000</ConvFactMuM>\n<ManMulDiv>M</ManMulDiv>\n<LookAheadWin> 0</LookAheadWin>\n<LoadingFactor>
  92 + \ 0.000</LoadingFactor>\n<SupplUnitCode/>\n<StorageSecurity/>\n<StorageHazard/>\n<StorageCondition/>\n<ProductShelfLife>
  93 + \ 0</ProductShelfLife>\n<InternalShelfLife> 0</InternalShelfLife>\n<AltMethodFlag/>\n<AltSisoFlag/>\n<AltReductionFlag/>\n<WithTaxExpenseType/>\n</InvMaster>\n "
  94 + http_version:
  95 + recorded_at: Fri, 06 Apr 2018 19:23:24 GMT
  96 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_query_query.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logon?CompanyId=L&CompanyPassword=&Operator=wland&OperatorPassword=piperita2016
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '36'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:23:35 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: '5810EEA000F1F04BA915BAF98CA700EE00 '
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:23:33 GMT
  36 +- request:
  37 + method: get
  38 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/Query/Query?BusinessObject=COMFND&UserId=5810EEA000F1F04BA915BAF98CA700EE00%20%20&XmlIn=%3C?xml%20version=%221.0%22%20encoding=%22Windows-1252%22?%3E%0A%3CQuery%20xmlns:xsd=%22http://www.w3.org/2001/XMLSchema-instance%22%20xsd:noNamespaceSchemaLocation=%22COMFND.XSD%22%3E%0A%20%20%3CTableName%3EInvMaster%3C/TableName%3E%0A%20%20%3CReturnRows%3E5%3C/ReturnRows%3E%0A%20%20%3CColumns%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%3CColumn%3EStockCode%3C/Column%3E%0A%20%20%20%20%0A%20%20%3C/Columns%3E%0A%20%20%0A%20%20%20%20%3CWhere%3E%0A%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%3CExpression%3E%0A%20%20%20%20%20%20%20%20%20%20%3COpenBracket%3E(%3C/OpenBracket%3E%0A%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%3CAndOr%3EAnd%3C/AndOr%3E%0A%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%3CColumn%3EStockCode%3C/Column%3E%0A%20%20%20%20%20%20%20%20%20%20%3CCondition%3EEQ%3C/Condition%3E%0A%20%20%20%20%20%20%20%20%20%20%3CValue%3E02%3C/Value%3E%0A%20%20%20%20%20%20%20%20%20%20%3CCloseBracket%3E)%3C/CloseBracket%3E%0A%20%20%20%20%20%20%20%20%3C/Expression%3E%0A%20%20%20%20%20%20%0A%20%20%20%20%3C/Where%3E%0A%20%20%0A%20%20%3COrderBy%3E%0A%20%20%20%20%3CColumn%3EStockCode%3C/Column%3E%0A%20%20%3C/OrderBy%3E%0A%3C/Query%3E%0A
  39 + body:
  40 + encoding: US-ASCII
  41 + string: ''
  42 + headers:
  43 + User-Agent:
  44 + - Syspro/7 RubyBindings/0.1.0
  45 + Content-Type:
  46 + - application/x-www-form-urlencoded
  47 + Accept-Encoding:
  48 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  49 + Accept:
  50 + - "*/*"
  51 + response:
  52 + status:
  53 + code: 200
  54 + message: OK
  55 + headers:
  56 + Content-Length:
  57 + - '436'
  58 + Content-Type:
  59 + - application/octet-stream
  60 + Server:
  61 + - Microsoft-HTTPAPI/2.0
  62 + Date:
  63 + - Fri, 06 Apr 2018 19:23:43 GMT
  64 + body:
  65 + encoding: UTF-8
  66 + string: "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n<InvMaster Language='05'
  67 + Language2='EN' CssStyle='' DecFormat='1' DateFormat='01' Role='01' Version='7.0.048'
  68 + OperatorPrimaryRole=' '>\n<HeaderDetails>\n<TableName>InvMaster</TableName>\n<Columns>\n<Column>StockCode</Column>\n</Columns>\n<OrderBy>\n<Column>StockCode</Column>\n</OrderBy>\n</HeaderDetails>\n<Row>\n<StockCode>02</StockCode>\n</Row>\n<RowsReturned>
  69 + \ 1</RowsReturned>\n</InvMaster>\n "
  70 + http_version:
  71 + recorded_at: Fri, 06 Apr 2018 19:23:41 GMT
  72 +recorded_with: VCR 4.0.0
... ...
test/cassettes/test_successful_logoff.yml 0 โ†’ 100644
  1 +---
  2 +http_interactions:
  3 +- request:
  4 + method: get
  5 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logon?CompanyId=L&CompanyPassword=&Operator=wland&OperatorPassword=piperita2016
  6 + body:
  7 + encoding: US-ASCII
  8 + string: ''
  9 + headers:
  10 + User-Agent:
  11 + - Syspro/7 RubyBindings/0.1.0
  12 + Content-Type:
  13 + - application/x-www-form-urlencoded
  14 + Accept-Encoding:
  15 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  16 + Accept:
  17 + - "*/*"
  18 + response:
  19 + status:
  20 + code: 200
  21 + message: OK
  22 + headers:
  23 + Content-Length:
  24 + - '36'
  25 + Content-Type:
  26 + - application/octet-stream
  27 + Server:
  28 + - Microsoft-HTTPAPI/2.0
  29 + Date:
  30 + - Fri, 06 Apr 2018 19:22:53 GMT
  31 + body:
  32 + encoding: UTF-8
  33 + string: '989D81EB0B184A499E7AC2E28724EE9000 '
  34 + http_version:
  35 + recorded_at: Fri, 06 Apr 2018 19:22:52 GMT
  36 +- request:
  37 + method: get
  38 + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logoff?UserId=989D81EB0B184A499E7AC2E28724EE9000%20%20
  39 + body:
  40 + encoding: US-ASCII
  41 + string: ''
  42 + headers:
  43 + User-Agent:
  44 + - Syspro/7 RubyBindings/0.1.0
  45 + Content-Type:
  46 + - application/x-www-form-urlencoded
  47 + Accept-Encoding:
  48 + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
  49 + Accept:
  50 + - "*/*"
  51 + response:
  52 + status:
  53 + code: 200
  54 + message: OK
  55 + headers:
  56 + Content-Length:
  57 + - '1'
  58 + Content-Type:
  59 + - application/octet-stream
  60 + Server:
  61 + - Microsoft-HTTPAPI/2.0
  62 + Date:
  63 + - Fri, 06 Apr 2018 19:22:53 GMT
  64 + body:
  65 + encoding: UTF-8
  66 + string: '0'
  67 + http_version:
  68 + recorded_at: Fri, 06 Apr 2018 19:22:52 GMT
  69 +recorded_with: VCR 4.0.0
... ...
test/client_test.rb
1   -require "test_helper"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'test_helper'
2 4  
3 5 class SysproClientTest < Minitest::Test
  6 + extend Minitest::Spec::DSL
  7 + before { VCR.insert_cassette name }
  8 + after { VCR.eject_cassette }
  9 +
  10 + let(:client) { ::Syspro::SysproClient.new }
  11 +
4 12 def test_get_syspro_version
5   - client = ::Syspro::SysproClient.new
6   - assert_match (/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/), client.get_syspro_version.version
  13 + assert_match(
  14 + /(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/,
  15 + client.get_syspro_version.version
  16 + )
7 17 end
8 18  
9 19 def test_client_block_execution
10   - client = ::Syspro::SysproClient.new
11   -
12   - version, resp = client.request {
  20 + version, resp = client.request do
13 21 Syspro::GetVersion.get_version
14   - }
  22 + end
15 23  
16   - assert_match version.version, resp.http_body
17   - assert_match (/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/), version.version
  24 + assert_match(version.version, resp.http_body)
  25 + assert_match(/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/, version.version)
18 26 end
19 27 end
... ...
test/logoff_test.rb
1   -require "test_helper"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'test_helper'
2 4  
3 5 class LogoffTest < Minitest::Test
4   - def test_successful_logoff
5   - username = "wland"
6   - password = "piperita2016"
7   - company = "L"
8   - company_password = ""
  6 + extend Minitest::Spec::DSL
  7 + before { VCR.insert_cassette name }
  8 + after { VCR.eject_cassette }
  9 +
  10 + let(:username) { 'wland' }
  11 + let(:password) { 'piperita2016' }
  12 + let(:company) { 'L' }
  13 + let(:company_password) { '' }
9 14  
  15 + def test_successful_logoff
10 16 uid = Syspro::Logon.logon(username, password, company, company_password)
11 17 assert_equal true, Syspro::Logoff.logoff(uid.guid)
12 18 end
13 19  
14 20 def test_logoff_error
15   - assert_kind_of String, Syspro::Logoff.logoff('1BB5B3050954BB459A5D034DB5CC386980')
  21 + assert_kind_of(
  22 + String,
  23 + Syspro::Logoff.logoff('1BB5B3050954BB459A5D034DB5CC386980')
  24 + )
16 25 end
17 26 end
18   -
... ...
test/logon_test.rb
1   -require "test_helper"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'test_helper'
2 4  
3 5 class LogonTest < Minitest::Test
4   - def test_logon
5   - username = "wland"
6   - password = "piperita2016"
7   - company = "L"
8   - company_password = ""
9   - client = ::Syspro::SysproClient.new
  6 + extend Minitest::Spec::DSL
  7 + before { VCR.insert_cassette name }
  8 + after { VCR.eject_cassette }
  9 +
  10 + let(:username) { 'wland' }
  11 + let(:password) { 'piperita2016' }
  12 + let(:company) { 'L' }
  13 + let(:company_password) { '' }
10 14  
11   - assert_match (/([A-Z0-9]{33})\w/), client.logon(username, password, company, company_password).guid
  15 + def test_logon
  16 + assert_match(
  17 + /([A-Z0-9]{33})\w/,
  18 + ::Syspro::SysproClient.new.logon(
  19 + username,
  20 + password,
  21 + company,
  22 + company_password
  23 + ).guid
  24 + )
12 25 end
13 26 end
14   -
... ...
test/query_test.rb 0 โ†’ 100644
  1 +# frozen_string_literal: true
  2 +
  3 +require 'test_helper'
  4 +
  5 +class QueryTest < Minitest::Test
  6 + extend Minitest::Spec::DSL
  7 + before { VCR.insert_cassette name }
  8 + after { VCR.eject_cassette }
  9 +
  10 + let(:username) { 'wland' }
  11 + let(:password) { 'piperita2016' }
  12 + let(:company) { 'L' }
  13 + let(:company_password) { '' }
  14 + let(:user_id) do
  15 + Syspro::Logon.logon(username, password, company, company_password)
  16 + end
  17 +
  18 + def test_query_browse # rubocop:disable Metrics/MethodLength
  19 + combrw = Syspro::BusinessObjects::ComBrw.new
  20 + combrw.browse_name = 'InvMaster'
  21 + combrw.start_condition = ''
  22 + combrw.return_rows = 5
  23 + combrw.filters = []
  24 + combrw.table_name = 'InvMaster'
  25 + combrw.title = 'StockCodes'
  26 + combrw.columns = [
  27 + { name: 'StockCode' }
  28 + ]
  29 +
  30 + browse_result = combrw.call(user_id.guid)
  31 +
  32 + refute_nil browse_result
  33 + end
  34 +
  35 + def test_query_query # rubocop:disable Metrics/MethodLength
  36 + comfnd = Syspro::BusinessObjects::ComFnd.new
  37 + comfnd.table_name = 'InvMaster'
  38 + comfnd.return_rows = 5
  39 + comfnd.columns = [
  40 + {
  41 + name: 'StockCode'
  42 + }
  43 + ]
  44 + comfnd.expressions = [
  45 + {
  46 + andor: 'And',
  47 + column: 'StockCode',
  48 + condition: 'EQ',
  49 + value: '02'
  50 + }
  51 + ]
  52 + comfnd.order_by = 'StockCode'
  53 +
  54 + query_result = comfnd.call(user_id.guid)
  55 +
  56 + refute_nil query_result
  57 + end
  58 +
  59 + def test_query_fetch
  60 + comfch = Syspro::BusinessObjects::ComFch.new
  61 + comfch.table_name = 'InvMaster'
  62 + comfch.key = '02'
  63 + comfch.optional_keys = []
  64 + comfch.full_key_provided = false
  65 + comfch.default_type = ''
  66 + comfch.espresso_fetch = true
  67 +
  68 + fetch_result = comfch.call(user_id.guid)
  69 +
  70 + refute_nil fetch_result
  71 + end
  72 +end
... ...
test/syspro_test.rb
1   -require "test_helper"
  1 +# frozen_string_literal: true
  2 +
  3 +require 'test_helper'
2 4  
3 5 class SysproTest < Minitest::Test
4 6 def test_that_it_has_a_version_number
... ...
test/test_helper.rb
1   -$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2   -require "syspro"
  1 +# frozen_string_literal: true
3 2  
4   -require "pry"
5   -require "minitest/autorun"
  3 +$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
  4 +require 'syspro'
  5 +
  6 +require 'pry'
  7 +require 'minitest/autorun'
  8 +require 'minitest-vcr'
  9 +require 'webmock'
  10 +
  11 +VCR.configure do |c|
  12 + c.cassette_library_dir = 'test/cassettes'
  13 + c.hook_into :webmock
  14 + # TODO: change passwords and move them to ENV
  15 + # c.filter_sensitive_data() { ENV[] }
  16 +end
  17 +
  18 +MinitestVcr::Spec.configure!
... ...