Commit dd338bea1c6322d58fa479e10011ab9d8281ef74
Committed by
GitHub
Merge pull request #1 from ike/logon
Logon, Logoff, and GetUserProfile
Showing
15 changed files
with
280 additions
and
113 deletions
Show diff stats
lib/syspro.rb
@@ -5,6 +5,10 @@ require "logger" | @@ -5,6 +5,10 @@ require "logger" | ||
5 | require "openssl" | 5 | require "openssl" |
6 | 6 | ||
7 | require "syspro/api_resource" | 7 | require "syspro/api_resource" |
8 | +require "syspro/get_logon_profile" | ||
9 | +require "syspro/get_version" | ||
10 | +require "syspro/logoff" | ||
11 | +require "syspro/logon" | ||
8 | require "syspro/syspro_client" | 12 | require "syspro/syspro_client" |
9 | require "syspro/singleton_api_resource" | 13 | require "syspro/singleton_api_resource" |
10 | require "syspro/syspro_object" | 14 | require "syspro/syspro_object" |
@@ -12,10 +16,8 @@ require "syspro/syspro_response" | @@ -12,10 +16,8 @@ require "syspro/syspro_response" | ||
12 | require "syspro/util" | 16 | require "syspro/util" |
13 | require "syspro/version" | 17 | require "syspro/version" |
14 | 18 | ||
15 | -require "syspro/api_operations/get_version" | ||
16 | require "syspro/api_operations/request" | 19 | require "syspro/api_operations/request" |
17 | 20 | ||
18 | - | ||
19 | module Syspro | 21 | module Syspro |
20 | @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest" | 22 | @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest" |
21 | 23 |
lib/syspro/api_operations/request.rb
1 | module Syspro | 1 | module Syspro |
2 | module ApiOperations | 2 | module ApiOperations |
3 | module Request | 3 | module Request |
4 | - def request(method, url, params = {}, opts = {}) | ||
5 | - client = SysproClient.active_client | 4 | + module ClassMethods |
5 | + def request(method, url, params = {}, opts = {}) | ||
6 | + warn_on_opts_in_params(params) | ||
7 | + | ||
8 | + opts = Util.normalize_opts(opts) | ||
9 | + opts[:client] ||= SysproClient.active_client | ||
6 | 10 | ||
7 | headers = opts.clone | 11 | headers = opts.clone |
12 | + user_id = headers.delete(:user_id) | ||
13 | + client = headers.delete(:client) | ||
14 | + # Assume all remaining opts must be headers | ||
8 | 15 | ||
9 | resp = client.execute_request( | 16 | resp = client.execute_request( |
10 | method, url, | 17 | method, url, |
11 | headers: headers, | 18 | headers: headers, |
19 | + user_id: user_id, | ||
12 | params: params | 20 | params: params |
13 | ) | 21 | ) |
14 | 22 | ||
15 | resp | 23 | resp |
16 | - end | 24 | + end |
17 | 25 | ||
18 | - def warn_on_opts_in_params(params) | 26 | + private |
27 | + | ||
28 | + def warn_on_opts_in_params(params) | ||
19 | Util::OPTS_USER_SPECIFIED.each do |opt| | 29 | Util::OPTS_USER_SPECIFIED.each do |opt| |
20 | if params.key?(opt) | 30 | if params.key?(opt) |
21 | $stderr.puts("WARNING: #{opt} should be in opts instead of params.") | 31 | $stderr.puts("WARNING: #{opt} should be in opts instead of params.") |
22 | end | 32 | end |
23 | end | 33 | end |
34 | + end | ||
35 | + end # ClassMethods | ||
36 | + | ||
37 | + def self.included(base) | ||
38 | + base.extend(ClassMethods) | ||
39 | + end | ||
40 | + | ||
41 | + protected | ||
42 | + | ||
43 | + def request(method, url, params = {}, opts = {}) | ||
44 | + opts = @opts.merge(Util.normalize_opts(opts)) | ||
45 | + self.class.request(method, url, params, opts) | ||
24 | end | 46 | end |
25 | end | 47 | end |
26 | end | 48 | end |
lib/syspro/api_resource.rb
1 | -require_relative "syspro_client" | ||
2 | -require_relative "api_operations/request" | 1 | +require "syspro/syspro_object" |
2 | +require "syspro/api_operations/request" | ||
3 | 3 | ||
4 | module Syspro | 4 | module Syspro |
5 | - class ApiResource < Syspro::SysproClient | 5 | + class ApiResource < SysproObject |
6 | + include Syspro::ApiOperations::Request | ||
6 | 7 | ||
7 | def self.class_name | 8 | def self.class_name |
8 | name.split("::")[-1] | 9 | name.split("::")[-1] |
9 | end | 10 | end |
10 | 11 | ||
11 | def self.resource_url | 12 | def self.resource_url |
12 | - if self == APIResource | 13 | + if self == ApiResource |
13 | raise NotImplementedError, "APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)" | 14 | raise NotImplementedError, "APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)" |
14 | end | 15 | end |
15 | - "/v1/#{CGI.escape(class_name.downcase)}s" | 16 | + "/#{CGI.escape(class_name.downcase)}" |
16 | end | 17 | end |
17 | 18 | ||
18 | def resource_url | 19 | def resource_url |
19 | - unless (id = self["id"]) | ||
20 | - raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", "id") | ||
21 | - end | ||
22 | - "#{self.class.resource_url}/#{CGI.escape(id)}" | 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)}" | ||
23 | end | 24 | end |
24 | 25 | ||
25 | def refresh | 26 | def refresh |
1 | +module Syspro | ||
2 | + class GetLogonProfile < ApiResource | ||
3 | + def self.get_logon_profile(user_id) | ||
4 | + params = { "UserId" => user_id } | ||
5 | + resp = self.request(:get, resource_url, params) | ||
6 | + parse_response(resp[0]) | ||
7 | + end | ||
8 | + | ||
9 | + def resource_url | ||
10 | + "/GetLogonProfile" | ||
11 | + end | ||
12 | + | ||
13 | + def self.parse_response(resp) | ||
14 | + doc = resp.data | ||
15 | + | ||
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, | ||
46 | + ) | ||
47 | + end | ||
48 | + 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 | + end | ||
58 | +end | ||
59 | + |
lib/syspro/api_operations/get_version.rb renamed to lib/syspro/get_version.rb
1 | -require_relative "request" | ||
2 | - | ||
3 | module Syspro | 1 | module Syspro |
4 | - module ApiOperations | ||
5 | - class GetVersion | ||
6 | - include ApiOperations::Request | 2 | + class GetVersion < ApiResource |
3 | + def self.get_version | ||
4 | + resp = self.request(:get, resource_url) | ||
5 | + VersionObject.new(resp[0].http_body) | ||
6 | + end | ||
7 | 7 | ||
8 | - def resource_url | ||
9 | - "/GetVersion" | ||
10 | - end | 8 | + def resource_url |
9 | + "/GetVersion" | ||
11 | end | 10 | end |
11 | + | ||
12 | + VersionObject = Struct.new(:version) | ||
12 | end | 13 | end |
13 | end | 14 | end |
14 | 15 |
1 | +module Syspro | ||
2 | + class Logoff < ApiResource | ||
3 | + def self.logoff(user_id) | ||
4 | + params = { "UserId" => user_id } | ||
5 | + resp = self.request(:get, resource_url, params) | ||
6 | + | ||
7 | + if resp[0].http_body == "0" | ||
8 | + true | ||
9 | + else | ||
10 | + resp[0].http_body | ||
11 | + end | ||
12 | + end | ||
13 | + | ||
14 | + def resource_url | ||
15 | + "/Logoff" | ||
16 | + end | ||
17 | + end | ||
18 | +end |
1 | +module Syspro | ||
2 | + class Logon < ApiResource | ||
3 | + def self.logon(username, password, company_id, company_password = nil) | ||
4 | + params = { | ||
5 | + "Operator" => username, | ||
6 | + "OperatorPassword" => password, | ||
7 | + "CompanyId" => company_id, | ||
8 | + "CompanyPassword" => company_password | ||
9 | + } | ||
10 | + resp = self.request(:get, resource_url, params) | ||
11 | + UserIdObject.new(resp[0].http_body) | ||
12 | + end | ||
13 | + | ||
14 | + def resource_url | ||
15 | + "/Logon" | ||
16 | + end | ||
17 | + | ||
18 | + UserIdObject = Struct.new(:guid) | ||
19 | + end | ||
20 | +end | ||
21 | + |
lib/syspro/singleton_api_resource.rb
@@ -6,7 +6,7 @@ module Syspro | @@ -6,7 +6,7 @@ module Syspro | ||
6 | if self == SingletonAPIResource | 6 | if self == SingletonAPIResource |
7 | raise NotImplementedError, "SingletonAPIResource is an abstract class. You should perform actions on its subclasses (Customer, etc.)" | 7 | raise NotImplementedError, "SingletonAPIResource is an abstract class. You should perform actions on its subclasses (Customer, etc.)" |
8 | end | 8 | end |
9 | - "/v1/#{CGI.escape(class_name.downcase)}" | 9 | + "/#{CGI.escape(class_name.downcase)}" |
10 | end | 10 | end |
11 | 11 | ||
12 | def resource_url | 12 | def resource_url |
lib/syspro/syspro_client.rb
@@ -2,11 +2,21 @@ module Syspro | @@ -2,11 +2,21 @@ module Syspro | ||
2 | class SysproClient | 2 | class SysproClient |
3 | attr_accessor :conn, :api_base | 3 | attr_accessor :conn, :api_base |
4 | 4 | ||
5 | + @verify_ssl_warned = false | ||
6 | + | ||
5 | def initialize(conn = nil) | 7 | def initialize(conn = nil) |
6 | self.conn = conn || self.class.default_conn | 8 | self.conn = conn || self.class.default_conn |
7 | @system_profiler = SystemProfiler.new | 9 | @system_profiler = SystemProfiler.new |
8 | end | 10 | end |
9 | 11 | ||
12 | + def logon(username, password, company_id, company_password) | ||
13 | + Syspro::Logon.logon(username, password, company_id, company_password) | ||
14 | + end | ||
15 | + | ||
16 | + def get_syspro_version | ||
17 | + Syspro::GetVersion.get_version | ||
18 | + end | ||
19 | + | ||
10 | def self.active_client | 20 | def self.active_client |
11 | Thread.current[:syspro_client] || default_client | 21 | Thread.current[:syspro_client] || default_client |
12 | end | 22 | end |
@@ -15,11 +25,6 @@ module Syspro | @@ -15,11 +25,6 @@ module Syspro | ||
15 | Thread.current[:syspro_client_default_client] ||= SysproClient.new(default_conn) | 25 | Thread.current[:syspro_client_default_client] ||= SysproClient.new(default_conn) |
16 | end | 26 | end |
17 | 27 | ||
18 | - def get_syspro_version | ||
19 | - version_getter = Syspro::ApiOperations::GetVersion.new | ||
20 | - version_getter.request(:get, version_getter.resource_url) | ||
21 | - end | ||
22 | - | ||
23 | # A default Faraday connection to be used when one isn't configured. This | 28 | # A default Faraday connection to be used when one isn't configured. This |
24 | # object should never be mutated, and instead instantiating your own | 29 | # object should never be mutated, and instead instantiating your own |
25 | # connection and wrapping it in a SysproClient object should be preferred. | 30 | # connection and wrapping it in a SysproClient object should be preferred. |
@@ -53,8 +58,27 @@ module Syspro | @@ -53,8 +58,27 @@ module Syspro | ||
53 | end | 58 | end |
54 | end | 59 | end |
55 | 60 | ||
56 | - def execute_request(method, path, api_base: nil, headers: {}, params: {}) | 61 | + # Executes the API call within the given block. Usage looks like: |
62 | + # | ||
63 | + # client = StripeClient.new | ||
64 | + # charge, resp = client.request { Charge.create } | ||
65 | + # | ||
66 | + def request | ||
67 | + @last_response = nil | ||
68 | + old_stripe_client = Thread.current[:stripe_client] | ||
69 | + Thread.current[:stripe_client] = self | ||
70 | + | ||
71 | + begin | ||
72 | + res = yield | ||
73 | + [res, @last_response] | ||
74 | + ensure | ||
75 | + Thread.current[:stripe_client] = old_stripe_client | ||
76 | + end | ||
77 | + end | ||
78 | + | ||
79 | + def execute_request(method, path, user_id: nil, api_base: nil, headers: {}, params: {}) | ||
57 | api_base ||= Syspro.api_base | 80 | api_base ||= Syspro.api_base |
81 | + user_id ||= "" | ||
58 | 82 | ||
59 | params = Util.objects_to_ids(params) | 83 | params = Util.objects_to_ids(params) |
60 | url = api_url(path, api_base) | 84 | url = api_url(path, api_base) |
@@ -82,6 +106,7 @@ module Syspro | @@ -82,6 +106,7 @@ module Syspro | ||
82 | context.body = body | 106 | context.body = body |
83 | context.method = method | 107 | context.method = method |
84 | context.path = path | 108 | context.path = path |
109 | + context.user_id = user_id | ||
85 | context.query_params = query_params ? Util.encode_parameters(query_params) : nil | 110 | context.query_params = query_params ? Util.encode_parameters(query_params) : nil |
86 | 111 | ||
87 | http_resp = execute_request_with_rescues(api_base, context) do | 112 | http_resp = execute_request_with_rescues(api_base, context) do |
@@ -238,6 +263,7 @@ module Syspro | @@ -238,6 +263,7 @@ module Syspro | ||
238 | attr_accessor :path | 263 | attr_accessor :path |
239 | attr_accessor :query_params | 264 | attr_accessor :query_params |
240 | attr_accessor :request_id | 265 | attr_accessor :request_id |
266 | + attr_accessor :user_id | ||
241 | 267 | ||
242 | # The idea with this method is that we might want to update some of | 268 | # The idea with this method is that we might want to update some of |
243 | # context information because a response that we've received from the API | 269 | # context information because a response that we've received from the API |
lib/syspro/syspro_object.rb
1 | module Syspro | 1 | module Syspro |
2 | class SysproObject | 2 | class SysproObject |
3 | + include Enumerable | ||
3 | 4 | ||
4 | - # Re-initializes the object based on a hash of values (usually one that's | ||
5 | - # come back from an API call). Adds or removes value accessors as necessary | ||
6 | - # and updates the state of internal data. | ||
7 | - # | ||
8 | - # Protected on purpose! Please do not expose. | ||
9 | - # | ||
10 | - # ==== Options | ||
11 | - # | ||
12 | - # * +:values:+ Hash used to update accessors and values. | ||
13 | - # * +:opts:+ Options for SysproObject like an API key. | ||
14 | - # * +:partial:+ Indicates that the re-initialization should not attempt to | ||
15 | - # remove accessors. | ||
16 | - def initialize_from(values, opts, partial = false) | 5 | + def initialize(id = nil, opts = {}) |
17 | @opts = Util.normalize_opts(opts) | 6 | @opts = Util.normalize_opts(opts) |
7 | + end | ||
18 | 8 | ||
19 | - # the `#send` is here so that we can keep this method private | ||
20 | - @original_values = self.class.send(:deep_copy, values) | ||
21 | - | ||
22 | - removed = partial ? Set.new : Set.new(@values.keys - values.keys) | ||
23 | - added = Set.new(values.keys - @values.keys) | ||
24 | - | ||
25 | - # Wipe old state before setting new. This is useful for e.g. updating a | ||
26 | - # customer, where there is no persistent card parameter. Mark those values | ||
27 | - # which don't persist as transient | ||
28 | - | ||
29 | - remove_accessors(removed) | ||
30 | - add_accessors(added, values) | 9 | + # Determines the equality of two Syspro objects. Syspro objects are |
10 | + # considered to be equal if they have the same set of values and each one | ||
11 | + # of those values is the same. | ||
12 | + def ==(other) | ||
13 | + other.is_a?(SysproObject) && @values == other.instance_variable_get(:@values) | ||
14 | + end | ||
31 | 15 | ||
32 | - removed.each do |k| | ||
33 | - @values.delete(k) | ||
34 | - @transient_values.add(k) | ||
35 | - @unsaved_values.delete(k) | ||
36 | - end | 16 | + def to_s(*_args) |
17 | + JSON.pretty_generate(to_hash) | ||
18 | + end | ||
37 | 19 | ||
38 | - update_attributes(values, opts, dirty: false) | ||
39 | - values.each_key do |k| | ||
40 | - @transient_values.delete(k) | ||
41 | - @unsaved_values.delete(k) | ||
42 | - end | 20 | + def inspect |
21 | + id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : "" | ||
22 | + "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values) | ||
23 | + end | ||
43 | 24 | ||
44 | - self | 25 | + def keys |
26 | + @values.keys | ||
45 | end | 27 | end |
46 | 28 | ||
47 | - def remove_accessors(keys) | ||
48 | - # not available in the #instance_eval below | ||
49 | - protected_fields = self.class.protected_fields | 29 | + def values |
30 | + @values.values | ||
31 | + end | ||
50 | 32 | ||
51 | - metaclass.instance_eval do | ||
52 | - keys.each do |k| | ||
53 | - next if protected_fields.include?(k) | ||
54 | - next if @@permanent_attributes.include?(k) | 33 | + def to_hash |
34 | + maybe_to_hash = lambda do |value| | ||
35 | + value && value.respond_to?(:to_hash) ? value.to_hash : value | ||
36 | + end | ||
55 | 37 | ||
56 | - # Remove methods for the accessor's reader and writer. | ||
57 | - [k, :"#{k}=", :"#{k}?"].each do |method_name| | ||
58 | - remove_method(method_name) if method_defined?(method_name) | ||
59 | - end | ||
60 | - end | 38 | + @values.each_with_object({}) do |(key, value), acc| |
39 | + acc[key] = case value | ||
40 | + when Array | ||
41 | + value.map(&maybe_to_hash) | ||
42 | + else | ||
43 | + maybe_to_hash.call(value) | ||
44 | + end | ||
61 | end | 45 | end |
62 | end | 46 | end |
63 | 47 | ||
64 | - def add_accessors(keys, values) | ||
65 | - # not available in the #instance_eval below | ||
66 | - protected_fields = self.class.protected_fields | ||
67 | - | ||
68 | - metaclass.instance_eval do | ||
69 | - keys.each do |k| | ||
70 | - next if protected_fields.include?(k) | ||
71 | - next if @@permanent_attributes.include?(k) | ||
72 | - | ||
73 | - if k == :method | ||
74 | - # Object#method is a built-in Ruby method that accepts a symbol | ||
75 | - # and returns the corresponding Method object. Because the API may | ||
76 | - # also use `method` as a field name, we check the arity of *args | ||
77 | - # to decide whether to act as a getter or call the parent method. | ||
78 | - define_method(k) { |*args| args.empty? ? @values[k] : super(*args) } | ||
79 | - else | ||
80 | - define_method(k) { @values[k] } | ||
81 | - end | ||
82 | - | ||
83 | - define_method(:"#{k}=") do |v| | ||
84 | - if v == "" | ||
85 | - raise ArgumentError, "You cannot set #{k} to an empty string. " \ | ||
86 | - "We interpret empty strings as nil in requests. " \ | ||
87 | - "You may set (object).#{k} = nil to delete the property." | ||
88 | - end | ||
89 | - @values[k] = Util.convert_to_stripe_object(v, @opts) | ||
90 | - dirty_value!(@values[k]) | ||
91 | - @unsaved_values.add(k) | ||
92 | - end | 48 | + def each(&blk) |
49 | + @values.each(&blk) | ||
50 | + end | ||
93 | 51 | ||
94 | - if [FalseClass, TrueClass].include?(values[k].class) | ||
95 | - define_method(:"#{k}?") { @values[k] } | ||
96 | - end | 52 | + private |
53 | + | ||
54 | + # Produces a deep copy of the given object including support for arrays, | ||
55 | + # hashes, and SysproObject. | ||
56 | + def self.deep_copy(obj) | ||
57 | + case obj | ||
58 | + when Array | ||
59 | + obj.map { |e| deep_copy(e) } | ||
60 | + when Hash | ||
61 | + obj.each_with_object({}) do |(k, v), copy| | ||
62 | + copy[k] = deep_copy(v) | ||
63 | + copy | ||
97 | end | 64 | end |
65 | + when SysproObject | ||
66 | + obj.class.construct_from( | ||
67 | + deep_copy(obj.instance_variable_get(:@values)), | ||
68 | + obj.instance_variable_get(:@opts).select do |k, _v| | ||
69 | + Util::OPTS_COPYABLE.include?(k) | ||
70 | + end | ||
71 | + ) | ||
72 | + else | ||
73 | + obj | ||
98 | end | 74 | end |
99 | end | 75 | end |
76 | + private_class_method :deep_copy | ||
77 | + | ||
100 | end | 78 | end |
101 | end | 79 | end |
lib/syspro/syspro_response.rb
@@ -26,8 +26,8 @@ module Syspro | @@ -26,8 +26,8 @@ module Syspro | ||
26 | # This may throw JSON::ParserError if the response body is not valid JSON. | 26 | # This may throw JSON::ParserError if the response body is not valid JSON. |
27 | def self.from_faraday_hash(http_resp) | 27 | def self.from_faraday_hash(http_resp) |
28 | resp = SysproResponse.new | 28 | resp = SysproResponse.new |
29 | - resp.data = Nokogiri::XML(http_resp[:body]) | ||
30 | resp.http_body = http_resp[:body] | 29 | resp.http_body = http_resp[:body] |
30 | + resp.data = Nokogiri::XML(resp.http_body) | ||
31 | resp.http_headers = http_resp[:headers] | 31 | resp.http_headers = http_resp[:headers] |
32 | resp.http_status = http_resp[:status] | 32 | resp.http_status = http_resp[:status] |
33 | resp.request_id = http_resp[:headers]["Request-Id"] | 33 | resp.request_id = http_resp[:headers]["Request-Id"] |
@@ -39,8 +39,8 @@ module Syspro | @@ -39,8 +39,8 @@ module Syspro | ||
39 | # This may throw JSON::ParserError if the response body is not valid JSON. | 39 | # This may throw JSON::ParserError if the response body is not valid JSON. |
40 | def self.from_faraday_response(http_resp) | 40 | def self.from_faraday_response(http_resp) |
41 | resp = SysproResponse.new | 41 | resp = SysproResponse.new |
42 | - resp.data = Nokogiri::XML(http_resp[:body]) | ||
43 | resp.http_body = http_resp.body | 42 | resp.http_body = http_resp.body |
43 | + resp.data = Nokogiri::XML(resp.http_body) | ||
44 | resp.http_headers = http_resp.headers | 44 | resp.http_headers = http_resp.headers |
45 | resp.http_status = http_resp.status | 45 | resp.http_status = http_resp.status |
46 | resp.request_id = http_resp.headers["Request-Id"] | 46 | resp.request_id = http_resp.headers["Request-Id"] |
lib/syspro/util.rb
@@ -125,6 +125,13 @@ module Syspro | @@ -125,6 +125,13 @@ module Syspro | ||
125 | end | 125 | end |
126 | end | 126 | end |
127 | 127 | ||
128 | + def self.url_encode(key) | ||
129 | + CGI.escape(key.to_s). | ||
130 | + # Don't use strict form encoding by changing the square bracket control | ||
131 | + # characters back to their literals. This is fine by the server, and | ||
132 | + # makes these parameter strings easier to read. | ||
133 | + gsub("%5B", "[").gsub("%5D", "]") | ||
134 | + end | ||
128 | end | 135 | end |
129 | end | 136 | end |
130 | 137 |
test/client_test.rb
@@ -3,6 +3,6 @@ require "test_helper" | @@ -3,6 +3,6 @@ require "test_helper" | ||
3 | class SysproClientTest < Minitest::Test | 3 | class SysproClientTest < Minitest::Test |
4 | def test_get_syspro_version | 4 | def test_get_syspro_version |
5 | client = ::Syspro::SysproClient.new | 5 | client = ::Syspro::SysproClient.new |
6 | - assert_match /(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/, client.get_syspro_version[0].http_body | 6 | + assert_match (/(\d+\.)?(\d+\.)?(\d+\.)?(\d+)/), client.get_syspro_version.version |
7 | end | 7 | end |
8 | end | 8 | end |
1 | +require "test_helper" | ||
2 | + | ||
3 | +class LogoffTest < Minitest::Test | ||
4 | + def test_successful_logoff | ||
5 | + username = "wland" | ||
6 | + password = "piperita2016" | ||
7 | + company = "L" | ||
8 | + company_password = "" | ||
9 | + | ||
10 | + uid = Syspro::Logon.logon(username, password, company, company_password) | ||
11 | + assert_equal true, Syspro::Logoff.logoff(uid.guid) | ||
12 | + end | ||
13 | + | ||
14 | + def test_logoff_error | ||
15 | + assert_kind_of String, Syspro::Logoff.logoff('1BB5B3050954BB459A5D034DB5CC386980') | ||
16 | + end | ||
17 | +end | ||
18 | + |
1 | +require "test_helper" | ||
2 | + | ||
3 | +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 | ||
10 | + | ||
11 | + assert_match (/([A-Z0-9]{33})\w/), client.logon(username, password, company, company_password).guid | ||
12 | + end | ||
13 | +end | ||
14 | + |