Commit 291179ed2e407c287e1359c1add0a9900164fa69
Committed by
GitHub
Merge pull request #2 from wildland/query
Query Endpoint
Showing
46 changed files
with
1499 additions
and
255 deletions
Show diff stats
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 | ... | ... |
Gemfile
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
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | - | ... | ... |
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
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! | ... | ... |