Commit 3d0157a52ebfc566a9df373c28c81f72dbf1a754

Authored by Isaac Lewis
1 parent 3c0896d2

add logoff

@@ -5,6 +5,9 @@ require "logger" @@ -5,6 +5,9 @@ require "logger"
5 require "openssl" 5 require "openssl"
6 6
7 require "syspro/api_resource" 7 require "syspro/api_resource"
  8 +require "syspro/get_version"
  9 +require "syspro/logoff"
  10 +require "syspro/logon"
8 require "syspro/syspro_client" 11 require "syspro/syspro_client"
9 require "syspro/singleton_api_resource" 12 require "syspro/singleton_api_resource"
10 require "syspro/syspro_object" 13 require "syspro/syspro_object"
@@ -12,10 +15,7 @@ require "syspro/syspro_response" @@ -12,10 +15,7 @@ require "syspro/syspro_response"
12 require "syspro/util" 15 require "syspro/util"
13 require "syspro/version" 16 require "syspro/version"
14 17
15 -require "syspro/api_operations/get_version"  
16 require "syspro/api_operations/request" 18 require "syspro/api_operations/request"
17 -require "syspro/api_operations/logon"  
18 -  
19 19
20 module Syspro 20 module Syspro
21 @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest" 21 @api_base = "http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest"
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
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  
7 -  
8 - def get_version  
9 - resp = self.request(:get, resource_url)  
10 - version = VersionObject.new(resp[0].http_body)  
11 - end  
12 -  
13 - def resource_url  
14 - "/GetVersion"  
15 - end 2 + class GetVersion < ApiResource
  3 + def self.get_version
  4 + resp = self.request(:get, resource_url)
  5 + version = VersionObject.new(resp[0].http_body)
  6 + end
16 7
17 - VersionObject = Struct.new(:version) 8 + def resource_url
  9 + "/GetVersion"
18 end 10 end
  11 +
  12 + VersionObject = Struct.new(:version)
19 end 13 end
20 end 14 end
21 15
lib/syspro/logoff.rb 0 โ†’ 100644
  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
lib/syspro/api_operations/logon.rb renamed to lib/syspro/logon.rb
1 -require_relative "request"  
2 -  
3 module Syspro 1 module Syspro
4 - module ApiOperations  
5 - class Logon  
6 - include ApiOperations::Request  
7 -  
8 - def logon(username, password, company_id, company_password = nil)  
9 - params = {  
10 - "Operator" => username,  
11 - "OperatorPassword" => password,  
12 - "CompanyId" => company_id,  
13 - "CompanyPassword" => company_password  
14 - }  
15 - resp = self.request(:get, resource_url, params)  
16 - user_id = UserIdObject.new(resp[0].http_body)  
17 - end  
18 -  
19 - def resource_url  
20 - "/Logon"  
21 - end 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 + user_id = UserIdObject.new(resp[0].http_body)
  12 + end
22 13
23 - UserIdObject = Struct.new(:guid) 14 + def resource_url
  15 + "/Logon"
24 end 16 end
  17 +
  18 + UserIdObject = Struct.new(:guid)
25 end 19 end
26 end 20 end
27 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
@@ -58,8 +58,27 @@ module Syspro @@ -58,8 +58,27 @@ module Syspro
58 end 58 end
59 end 59 end
60 60
61 - 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: {})
62 api_base ||= Syspro.api_base 80 api_base ||= Syspro.api_base
  81 + user_id ||= ""
63 82
64 params = Util.objects_to_ids(params) 83 params = Util.objects_to_ids(params)
65 url = api_url(path, api_base) 84 url = api_url(path, api_base)
@@ -87,6 +106,7 @@ module Syspro @@ -87,6 +106,7 @@ module Syspro
87 context.body = body 106 context.body = body
88 context.method = method 107 context.method = method
89 context.path = path 108 context.path = path
  109 + context.user_id = user_id
90 context.query_params = query_params ? Util.encode_parameters(query_params) : nil 110 context.query_params = query_params ? Util.encode_parameters(query_params) : nil
91 111
92 http_resp = execute_request_with_rescues(api_base, context) do 112 http_resp = execute_request_with_rescues(api_base, context) do
@@ -243,6 +263,7 @@ module Syspro @@ -243,6 +263,7 @@ module Syspro
243 attr_accessor :path 263 attr_accessor :path
244 attr_accessor :query_params 264 attr_accessor :query_params
245 attr_accessor :request_id 265 attr_accessor :request_id
  266 + attr_accessor :user_id
246 267
247 # 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
248 # 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