Commit 0c0af54a8bd446733ec29ed87509d6c12b68d578
1 parent
eef01045
error handling; cleanup; rename to `syspro-ruby`
Showing
13 changed files
with
131 additions
and
54 deletions
Show diff stats
Gemfile
@@ -2,5 +2,4 @@ source "https://rubygems.org" | @@ -2,5 +2,4 @@ source "https://rubygems.org" | ||
2 | 2 | ||
3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } |
4 | 4 | ||
5 | -# Specify your gem's dependencies in syspro.gemspec | ||
6 | gemspec | 5 | gemspec |
README.md
1 | -# Syspro | 1 | +# syspro-ruby |
2 | 2 | ||
3 | -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/syspro`. To experiment with that code, run `bin/console` for an interactive prompt. | ||
4 | - | ||
5 | -TODO: Delete this and the text above, and describe your gem | 3 | +syspro-ruby is an adapter gem to connect to SYSPRO 7 ERP installations. You can use this gem to connect to the SYSPRO 7 WCF Service and build Ruby applications on top of your SYSPRO data. |
6 | 4 | ||
7 | ## Installation | 5 | ## Installation |
8 | 6 | ||
@@ -22,7 +20,57 @@ Or install it yourself as: | @@ -22,7 +20,57 @@ Or install it yourself as: | ||
22 | 20 | ||
23 | ## Usage | 21 | ## Usage |
24 | 22 | ||
25 | -TODO: Write usage instructions here | 23 | +### Utilities |
24 | + | ||
25 | +#### Logon | ||
26 | + | ||
27 | +```rb | ||
28 | +user_id = Syspro::Logon.logon(username, password, company_id, company_password) | ||
29 | +``` | ||
30 | +`user_id` will be a `UserId` object that contains the `guid` supplied by SYSPRO. You will use this guid to make further requests to SYSPRO. | ||
31 | + | ||
32 | +#### GetUserProfile | ||
33 | + | ||
34 | +```rb | ||
35 | +user_profile = Syspro::GetUserProfile.get_user_profile(guid) | ||
36 | +``` | ||
37 | +`user_profile` will be a `UserProfile` object that contains the following: | ||
38 | + - `company_name` | ||
39 | + - `operator_code` | ||
40 | + - `operator_group` | ||
41 | + - `operator_email_address` | ||
42 | + - `operator_location` | ||
43 | + - `operator_language_code` | ||
44 | + - `system_language` | ||
45 | + - `accounting_date` | ||
46 | + - `company_date` | ||
47 | + - `default_ar_branch` | ||
48 | + - `default_ap_branch` | ||
49 | + - `default_bank` | ||
50 | + - `default_warehouse` | ||
51 | + - `default_customer` | ||
52 | + - `system_site_id` | ||
53 | + - `system_nationality_code` | ||
54 | + - `local_currency_code` | ||
55 | + - `currency_description` | ||
56 | + - `default_requisition_user` | ||
57 | + - `xml_to_html_transform` | ||
58 | + - `css_style` | ||
59 | + - `css_suffix` | ||
60 | + - `decimal_format` | ||
61 | + - `date_format` | ||
62 | + - `functional_role` | ||
63 | + - `database_type` | ||
64 | + - `syspro_version` | ||
65 | + - `enet_version` | ||
66 | + - `syspro_server_bit_width` | ||
67 | + | ||
68 | +#### Logoff | ||
69 | + | ||
70 | +```rb | ||
71 | +logged_off = Syspro::Logoff.logoff(guid) | ||
72 | +``` | ||
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. | ||
26 | 74 | ||
27 | ## Development | 75 | ## Development |
28 | 76 | ||
@@ -32,7 +80,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To | @@ -32,7 +80,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To | ||
32 | 80 | ||
33 | ## Contributing | 81 | ## Contributing |
34 | 82 | ||
35 | -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/syspro. 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. | 83 | +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. |
36 | 84 | ||
37 | ## License | 85 | ## License |
38 | 86 | ||
@@ -40,4 +88,4 @@ The gem is available as open source under the terms of the [MIT License](https:/ | @@ -40,4 +88,4 @@ The gem is available as open source under the terms of the [MIT License](https:/ | ||
40 | 88 | ||
41 | ## Code of Conduct | 89 | ## Code of Conduct |
42 | 90 | ||
43 | -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/[USERNAME]/syspro/blob/master/CODE_OF_CONDUCT.md). | 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). |
bin/console
@@ -2,13 +2,6 @@ | @@ -2,13 +2,6 @@ | ||
2 | 2 | ||
3 | require "bundler/setup" | 3 | require "bundler/setup" |
4 | require "syspro" | 4 | require "syspro" |
5 | +require "pry" | ||
5 | 6 | ||
6 | -# You can add fixtures and/or initialization code here to make experimenting | ||
7 | -# with your gem easier. You can also use a different console, if you like. | ||
8 | - | ||
9 | -# (If you use this, don't forget to add pry to your Gemfile!) | ||
10 | -# require "pry" | ||
11 | -# Pry.start | ||
12 | - | ||
13 | -require "irb" | ||
14 | -IRB.start(__FILE__) | 7 | +Pry.start |
bin/setup
lib/syspro.rb
@@ -5,6 +5,7 @@ require "logger" | @@ -5,6 +5,7 @@ require "logger" | ||
5 | require "openssl" | 5 | require "openssl" |
6 | 6 | ||
7 | require "syspro/api_resource" | 7 | require "syspro/api_resource" |
8 | +require "syspro/errors" | ||
8 | require "syspro/get_logon_profile" | 9 | require "syspro/get_logon_profile" |
9 | require "syspro/get_version" | 10 | require "syspro/get_version" |
10 | require "syspro/logoff" | 11 | require "syspro/logoff" |
lib/syspro/api_resource.rb
@@ -16,13 +16,6 @@ module Syspro | @@ -16,13 +16,6 @@ module Syspro | ||
16 | "/#{CGI.escape(class_name.downcase)}" | 16 | "/#{CGI.escape(class_name.downcase)}" |
17 | end | 17 | end |
18 | 18 | ||
19 | - def resource_url | ||
20 | - #unless (id = self["id"]) | ||
21 | - #raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", "id") | ||
22 | - #end | ||
23 | - #"#{self.class.resource_url}/#{CGI.escape(id)}" | ||
24 | - end | ||
25 | - | ||
26 | def refresh | 19 | def refresh |
27 | resp, opts = request(:get, resource_url, @retrieve_params) | 20 | resp, opts = request(:get, resource_url, @retrieve_params) |
28 | initialize_from(resp.data, opts) | 21 | initialize_from(resp.data, opts) |
1 | +module Syspro | ||
2 | + class SysproError < StandardError | ||
3 | + attr_reader :message, :response, :code, :http_body, :http_headers, | ||
4 | + :http_status, :data, :request_id | ||
5 | + | ||
6 | + # Initializes a SysproError. | ||
7 | + def initialize(message = nil, http_status: nil, http_body: nil, data: nil, | ||
8 | + http_headers: nil, code: nil) | ||
9 | + @message = message | ||
10 | + @http_status = http_status | ||
11 | + @http_body = http_body | ||
12 | + @http_headers = http_headers || {} | ||
13 | + @data = data | ||
14 | + @code = code | ||
15 | + @request_id = @http_headers[:request_id] | ||
16 | + end | ||
17 | + | ||
18 | + def to_s | ||
19 | + status_string = @http_status.nil? ? "" : "(Status #{@http_status}) " | ||
20 | + id_string = @request_id.nil? ? "" : "(Request #{@request_id}) " | ||
21 | + "#{status_string}#{id_string}#{@message}" | ||
22 | + end | ||
23 | + end | ||
24 | + | ||
25 | + class AuthenticationError < SysproError | ||
26 | + end | ||
27 | + | ||
28 | + class ApiConnectionError < SysproError | ||
29 | + end | ||
30 | + | ||
31 | + class ApiError < SysproError | ||
32 | + end | ||
33 | + | ||
34 | +end |
lib/syspro/syspro_client.rb
@@ -40,6 +40,7 @@ module Syspro | @@ -40,6 +40,7 @@ module Syspro | ||
40 | c.adapter Faraday.default_adapter | 40 | c.adapter Faraday.default_adapter |
41 | end | 41 | end |
42 | 42 | ||
43 | + # For now, we're not verifying SSL certificates. The warning will appear. | ||
43 | #if Syspro.verify_ssl_certs | 44 | #if Syspro.verify_ssl_certs |
44 | #conn.ssl.verify = true | 45 | #conn.ssl.verify = true |
45 | #conn.ssl.cert_store = Syspro.ca_store | 46 | #conn.ssl.cert_store = Syspro.ca_store |
@@ -129,7 +130,7 @@ module Syspro | @@ -129,7 +130,7 @@ module Syspro | ||
129 | end | 130 | end |
130 | 131 | ||
131 | def general_api_error(status, body) | 132 | def general_api_error(status, body) |
132 | - APIError.new("Invalid response object from API: #{body.inspect} " \ | 133 | + ApiError.new("Invalid response object from API: #{body.inspect} " \ |
133 | "(HTTP response code was #{status})", | 134 | "(HTTP response code was #{status})", |
134 | http_status: status, http_body: body) | 135 | http_status: status, http_body: body) |
135 | end | 136 | end |
lib/syspro/syspro_object.rb
@@ -4,6 +4,14 @@ module Syspro | @@ -4,6 +4,14 @@ module Syspro | ||
4 | 4 | ||
5 | def initialize(id = nil, opts = {}) | 5 | def initialize(id = nil, opts = {}) |
6 | @opts = Util.normalize_opts(opts) | 6 | @opts = Util.normalize_opts(opts) |
7 | + @original_values = {} | ||
8 | + @values = {} | ||
9 | + | ||
10 | + # This really belongs in APIResource, but not putting it there allows us | ||
11 | + # to have a unified inspect method | ||
12 | + @unsaved_values = Set.new | ||
13 | + @transient_values = Set.new | ||
14 | + @values[:id] = id if id | ||
7 | end | 15 | end |
8 | 16 | ||
9 | # Determines the equality of two Syspro objects. Syspro objects are | 17 | # Determines the equality of two Syspro objects. Syspro objects are |
@@ -52,7 +60,7 @@ module Syspro | @@ -52,7 +60,7 @@ module Syspro | ||
52 | private | 60 | private |
53 | 61 | ||
54 | # Produces a deep copy of the given object including support for arrays, | 62 | # Produces a deep copy of the given object including support for arrays, |
55 | - # hashes, and SysproObject. | 63 | + # hashes, and SysproObjects. |
56 | def self.deep_copy(obj) | 64 | def self.deep_copy(obj) |
57 | case obj | 65 | case obj |
58 | when Array | 66 | when Array |
lib/syspro/syspro_response.rb
1 | require "nokogiri" | 1 | require "nokogiri" |
2 | 2 | ||
3 | module Syspro | 3 | module Syspro |
4 | - # SysproResponse encapsulates some vitals of a response that came back from | ||
5 | - # the Syspro API. | ||
6 | class SysproResponse | 4 | class SysproResponse |
7 | - # The data contained by the HTTP body of the response deserialized from | ||
8 | - # JSON. | ||
9 | - attr_accessor :data | ||
10 | - | ||
11 | - # The raw HTTP body of the response. | ||
12 | - attr_accessor :http_body | ||
13 | - | ||
14 | - # A Hash of the HTTP headers of the response. | ||
15 | - attr_accessor :http_headers | ||
16 | - | ||
17 | - # The integer HTTP status code of the response. | ||
18 | - attr_accessor :http_status | ||
19 | - | ||
20 | - # The Syspro request ID of the response. | ||
21 | - attr_accessor :request_id | 5 | + attr_accessor :data, :http_body, :http_headers, :http_status, :request_id |
22 | 6 | ||
23 | # Initializes a SysproResponse object from a Hash like the kind returned as | 7 | # Initializes a SysproResponse object from a Hash like the kind returned as |
24 | # part of a Faraday exception. | 8 | # part of a Faraday exception. |
25 | - # | ||
26 | - # This may throw JSON::ParserError if the response body is not valid JSON. | ||
27 | def self.from_faraday_hash(http_resp) | 9 | def self.from_faraday_hash(http_resp) |
28 | resp = SysproResponse.new | 10 | resp = SysproResponse.new |
29 | resp.http_body = http_resp[:body] | 11 | resp.http_body = http_resp[:body] |
@@ -35,8 +17,6 @@ module Syspro | @@ -35,8 +17,6 @@ module Syspro | ||
35 | end | 17 | end |
36 | 18 | ||
37 | # Initializes a SysproResponse object from a Faraday HTTP response object. | 19 | # Initializes a SysproResponse object from a Faraday HTTP response object. |
38 | - # | ||
39 | - # This may throw JSON::ParserError if the response body is not valid JSON. | ||
40 | def self.from_faraday_response(http_resp) | 20 | def self.from_faraday_response(http_resp) |
41 | resp = SysproResponse.new | 21 | resp = SysproResponse.new |
42 | resp.http_body = http_resp.body | 22 | resp.http_body = http_resp.body |
lib/syspro/util.rb
@@ -2,9 +2,21 @@ module Syspro | @@ -2,9 +2,21 @@ module Syspro | ||
2 | class Util | 2 | class Util |
3 | # Options that a user is allowed to specify. | 3 | # Options that a user is allowed to specify. |
4 | OPTS_USER_SPECIFIED = Set[ | 4 | OPTS_USER_SPECIFIED = Set[ |
5 | - # :syspro_version | 5 | + :user_id |
6 | ].freeze | 6 | ].freeze |
7 | 7 | ||
8 | + # Options that should be copyable from one StripeObject to another | ||
9 | + # including options that may be internal. | ||
10 | + OPTS_COPYABLE = ( | ||
11 | + OPTS_USER_SPECIFIED + Set[:api_base] | ||
12 | + ).freeze | ||
13 | + | ||
14 | + # Options that should be persisted between API requests. This includes | ||
15 | + # client, which is an object containing an HTTP client to reuse. | ||
16 | + OPTS_PERSISTABLE = ( | ||
17 | + OPTS_USER_SPECIFIED + Set[:client] | ||
18 | + ).freeze | ||
19 | + | ||
8 | 20 | ||
9 | def self.objects_to_ids(h) | 21 | def self.objects_to_ids(h) |
10 | case h | 22 | case h |
@@ -45,8 +57,7 @@ module Syspro | @@ -45,8 +57,7 @@ module Syspro | ||
45 | end | 57 | end |
46 | end | 58 | end |
47 | 59 | ||
48 | - | ||
49 | - # The secondary opts argument can either be a string or hash | 60 | + # The secondary opts argument can either be a string or hash |
50 | # Turn this value into an api_key and a set of headers | 61 | # Turn this value into an api_key and a set of headers |
51 | def self.normalize_opts(opts) | 62 | def self.normalize_opts(opts) |
52 | case opts | 63 | case opts |
syspro.gemspec renamed to syspro-ruby.gemspec
@@ -3,10 +3,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | @@ -3,10 +3,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | ||
3 | require "syspro/version" | 3 | require "syspro/version" |
4 | 4 | ||
5 | Gem::Specification.new do |spec| | 5 | Gem::Specification.new do |spec| |
6 | - spec.name = "syspro" | 6 | + spec.name = "syspro-ruby" |
7 | spec.version = Syspro::VERSION | 7 | spec.version = Syspro::VERSION |
8 | spec.authors = ["Isaac Lewis"] | 8 | spec.authors = ["Isaac Lewis"] |
9 | - spec.email = ["ike@wild.land"] | 9 | + spec.email = ["isaac@ike.io"] |
10 | 10 | ||
11 | spec.summary = %q{SYSPRO 7 Api Ruby adapter} | 11 | spec.summary = %q{SYSPRO 7 Api Ruby adapter} |
12 | spec.license = "MIT" | 12 | spec.license = "MIT" |
test/client_test.rb
@@ -5,4 +5,15 @@ class SysproClientTest < Minitest::Test | @@ -5,4 +5,15 @@ class SysproClientTest < Minitest::Test | ||
5 | client = ::Syspro::SysproClient.new | 5 | client = ::Syspro::SysproClient.new |
6 | assert_match (/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/), client.get_syspro_version.version | 6 | assert_match (/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/), client.get_syspro_version.version |
7 | end | 7 | end |
8 | + | ||
9 | + def test_client_block_execution | ||
10 | + client = ::Syspro::SysproClient.new | ||
11 | + | ||
12 | + version, resp = client.request { | ||
13 | + Syspro::GetVersion.get_version | ||
14 | + } | ||
15 | + | ||
16 | + assert_match version.version, resp.http_body | ||
17 | + assert_match (/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/), version.version | ||
18 | + end | ||
8 | end | 19 | end |