innards 0.1.0.pre → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +49 -0
- data/README.md +26 -6
- data/Rakefile +2 -0
- data/innards.gemspec +26 -0
- data/lib/innards.rb +9 -1
- data/lib/innards/connection.rb +198 -109
- data/lib/innards/cookie_handler.rb +24 -0
- data/lib/innards/digest_handler.rb +93 -0
- data/lib/innards/exceptions.rb +31 -4
- data/lib/innards/multipart_handler.rb +31 -0
- data/lib/innards/parsers/get_metadata_parser.rb +64 -0
- data/lib/innards/parsers/login_parser.rb +29 -0
- data/lib/innards/parsers/logout_parser.rb +29 -0
- data/lib/innards/parsers/parser_base.rb +73 -0
- data/lib/innards/parsers/search_parser.rb +41 -0
- data/lib/innards/version.rb +2 -2
- data/spec/innards/connection_spec.rb +232 -0
- data/spec/innards/cookie_handler_spec.rb +16 -0
- data/spec/innards/digest_handler_spec.rb +31 -0
- data/spec/innards/multipart_handler_spec.rb +28 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/stubs/getobject_success.txt +14 -0
- data/spec/stubs/login_success.xml +14 -0
- data/spec/stubs/logout_success.xml +7 -0
- data/spec/stubs/metadata_success.xml +127 -0
- data/spec/stubs/rets_error.xml +1 -0
- data/spec/stubs/search_success.xml +10 -0
- data/tasks/util.rake +9 -0
- metadata +64 -34
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Innards
|
4
|
+
|
5
|
+
# Handles Digest Authentication
|
6
|
+
class CookieHandler
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@params = params
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse cookie_header
|
13
|
+
cookie_header.scan(/(\w+)=(.+?);/) {
|
14
|
+
@params[:cookies].push([$1, $2])
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def build
|
19
|
+
cookies = @params[:cookies]
|
20
|
+
URI.encode_www_form(cookies) unless cookies.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Innards
|
2
|
+
|
3
|
+
# Handles Digest Authentication
|
4
|
+
class DigestHandler
|
5
|
+
|
6
|
+
include Innards::Exceptions
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@params = params
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse www_authenticate_header
|
13
|
+
digest = {}
|
14
|
+
www_authenticate_header.scan(/(\w+)="(.*?)"/) {
|
15
|
+
digest[$1] = $2
|
16
|
+
}
|
17
|
+
if www_authenticate_header =~ /stale=true$/
|
18
|
+
digest[:stale] = true
|
19
|
+
raise StaleDigestException.new
|
20
|
+
end
|
21
|
+
@params[:digest] = digest
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_for path
|
25
|
+
digest = @params[:digest]
|
26
|
+
return nil if digest.empty?
|
27
|
+
|
28
|
+
cnonce = generate_md5_hash(random)
|
29
|
+
header = [
|
30
|
+
%Q(Digest username="#{@params[:login_uri].user}"),
|
31
|
+
%Q(realm="#{digest['realm']}"),
|
32
|
+
%Q(nonce="#{digest['nonce']}"),
|
33
|
+
%Q(uri="#{path}"),
|
34
|
+
%Q(response="#{build_digest(path, cnonce)}"),
|
35
|
+
]
|
36
|
+
|
37
|
+
if has_qop?
|
38
|
+
fields = [
|
39
|
+
%Q(cnonce="#{cnonce}"),
|
40
|
+
%Q(qop="#{digest['qop']}"),
|
41
|
+
%Q(nc=#{request_nc})
|
42
|
+
]
|
43
|
+
fields.each { |field| header << field }
|
44
|
+
end
|
45
|
+
|
46
|
+
header << %Q(opaque="#{digest['opaque']}") if has_opaque?
|
47
|
+
header.join(", ")
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_digest path, cnonce
|
51
|
+
digest = @params[:digest]
|
52
|
+
digest_parts = [
|
53
|
+
generate_md5_hash(generate_ha1_hash),
|
54
|
+
digest['nonce'],
|
55
|
+
generate_md5_hash(generate_ha2_hash(path))]
|
56
|
+
digest_parts.insert(2, request_nc, cnonce, digest['qop']) if has_qop?
|
57
|
+
generate_md5_hash(digest_parts.join(":"))
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_opaque?
|
61
|
+
digest = @params[:digest]
|
62
|
+
digest.has_key?('opaque') and !digest['opaque'].empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_qop?
|
66
|
+
digest = @params[:digest]
|
67
|
+
digest.has_key?('qop') and !digest['qop'].empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def generate_ha1_hash
|
71
|
+
login_uri = @params[:login_uri]
|
72
|
+
digest = @params[:digest]
|
73
|
+
[login_uri.user, digest['realm'], login_uri.password].join(":")
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_ha2_hash(path)
|
77
|
+
["GET", path].join(":")
|
78
|
+
end
|
79
|
+
|
80
|
+
def request_nc
|
81
|
+
"%08d" % @params[:request_count]
|
82
|
+
end
|
83
|
+
|
84
|
+
def random
|
85
|
+
"%x" % (Time.now.to_i + rand(65535))
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_md5_hash(str)
|
89
|
+
Digest::MD5.hexdigest(str)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
data/lib/innards/exceptions.rb
CHANGED
@@ -1,8 +1,35 @@
|
|
1
1
|
module Innards
|
2
2
|
module Exceptions
|
3
|
-
|
4
|
-
|
5
|
-
end
|
6
|
-
|
3
|
+
|
4
|
+
# Thrown when a invalid URL is passed
|
5
|
+
class InvalidUrlException < StandardError; end
|
6
|
+
|
7
|
+
# Thrown when a invalid/unsupported RETS Version is passed
|
8
|
+
class InvalidRetsVersionException < StandardError; end
|
9
|
+
|
10
|
+
# Thrown when a invalid Auth Mode is passed
|
11
|
+
class InvalidAuthModeException < StandardError; end
|
12
|
+
|
13
|
+
# Thrown when a invalid User Agent is passed
|
14
|
+
class InvalidUserAgentException < StandardError; end
|
15
|
+
|
16
|
+
# Thrown when the rets conection requires authentication
|
17
|
+
class AuthenticationRequiredException < StandardError; end
|
18
|
+
|
19
|
+
# Thrown when digest data is stale and the request needs to be retried
|
20
|
+
class StaleDigestException < StandardError; end
|
21
|
+
|
22
|
+
# Thrown when a function is called which requres a logged in session
|
23
|
+
class LoginRequiredException < StandardError; end
|
24
|
+
|
25
|
+
# Invalid Search Type
|
26
|
+
class InvalidSearchTypeException < StandardError; end
|
27
|
+
|
28
|
+
# Invalid Search Type
|
29
|
+
class InvalidClassException < StandardError; end
|
30
|
+
|
31
|
+
# Invalid Search Type
|
32
|
+
class InvalidQueryException < StandardError; end
|
33
|
+
|
7
34
|
end
|
8
35
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'multipart_parser/reader'
|
2
|
+
|
3
|
+
module Innards
|
4
|
+
|
5
|
+
# Handles Digest Authentication
|
6
|
+
class MultipartHandler
|
7
|
+
|
8
|
+
def initialize(content_type)
|
9
|
+
@boundary = MultipartParser::Reader.extract_boundary_value(content_type)
|
10
|
+
rescue MultipartParser::NotMultipartError
|
11
|
+
@boundary = ""
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse body
|
15
|
+
parts = []
|
16
|
+
multipart_reader = MultipartParser::Reader.new @boundary
|
17
|
+
multipart_reader.on_part do |part|
|
18
|
+
part.on_data do |data|
|
19
|
+
parts << part.headers.merge({:data => data})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
multipart_reader.write body.strip
|
23
|
+
parts
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_multipart_response?
|
27
|
+
!@boundary.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
module Innards
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
# SAX Parser for RETS Metadata
|
6
|
+
class GetMetadataParser < ParserBase
|
7
|
+
|
8
|
+
METADATA_SECTIONS = {
|
9
|
+
"METADATA-RESOURCE" => :resources,
|
10
|
+
"METADATA-CLASS" => :classes,
|
11
|
+
"METADATA-TABLE" => :tables,
|
12
|
+
"METADATA-OBJECT" => :objects,
|
13
|
+
"METADATA-SEARCH_HELP" => :search_help,
|
14
|
+
"METADATA-EDITMASK" => :edit_masks,
|
15
|
+
"METADATA-LOOKUP" => :lookups,
|
16
|
+
"METADATA-LOOKUP_TYPE" => :lookup_types
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super()
|
21
|
+
@current_builder = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_element(name)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def end_element(name)
|
29
|
+
super
|
30
|
+
if METADATA_SECTIONS.has_key?(name.to_s)
|
31
|
+
@current_builder = {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def attr(name, value)
|
36
|
+
super
|
37
|
+
METADATA_SECTIONS.each_key do |section|
|
38
|
+
if switch_active?(:"#{section}")
|
39
|
+
@current_builder[name.to_sym] = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def text(value)
|
45
|
+
super
|
46
|
+
|
47
|
+
if switch_active?(:COLUMNS)
|
48
|
+
@current_builder[:columns] = data_splitter(value)
|
49
|
+
end
|
50
|
+
|
51
|
+
METADATA_SECTIONS.each_pair do |section, symbol|
|
52
|
+
if switch_active?(:"#{section}") and switch_active?(:DATA)
|
53
|
+
parsed_data = data_merger(@current_builder[:columns], data_splitter(value))
|
54
|
+
filtered = @current_builder.reject{ |key| key == :columns }
|
55
|
+
|
56
|
+
@metadata[symbol] = [] unless @metadata.has_key?(symbol)
|
57
|
+
@metadata[symbol].push parsed_data.merge(filtered)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module Innards
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
# SAX Parser for RETS Login Response
|
6
|
+
class LoginParser < ParserBase
|
7
|
+
|
8
|
+
def start_element(name)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def end_element(name)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def attr(name, value)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def text(value)
|
21
|
+
super
|
22
|
+
if switch_active?(:RETS)
|
23
|
+
@metadata.merge! split_multiline_key_value_pairs(value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module Innards
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
# SAX Parser for RETS Logout Response
|
6
|
+
class LogoutParser < ParserBase
|
7
|
+
|
8
|
+
def start_element(name)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def end_element(name)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def attr(name, value)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def text(value)
|
21
|
+
super
|
22
|
+
if switch_active?(:"RETS-RESPONSE")
|
23
|
+
@metadata.merge! split_multiline_key_value_pairs(value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'ox'
|
3
|
+
|
4
|
+
module Innards
|
5
|
+
module Parsers
|
6
|
+
|
7
|
+
# Base Parser from which all RETS Response Parsers are based
|
8
|
+
class ParserBase < ::Ox::Sax
|
9
|
+
|
10
|
+
attr_reader :rets_response, :metadata
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@switches = {}
|
14
|
+
@metadata = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def start_element(name)
|
18
|
+
element_tracker_switch name, true
|
19
|
+
end
|
20
|
+
|
21
|
+
def end_element(name)
|
22
|
+
element_tracker_switch name, false
|
23
|
+
end
|
24
|
+
|
25
|
+
def attr(name, value)
|
26
|
+
if switch_active?(:RETS)
|
27
|
+
@rets_response = {} unless @rets_response.kind_of?(Hash)
|
28
|
+
@rets_response[name] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def text(value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def split_multiline_key_value_pairs value
|
36
|
+
result = {}
|
37
|
+
value.scan(/(\w+) = (.*?)$/) do |match|
|
38
|
+
result[match[0].to_sym] = match[1].gsub(/http:\/\/.+?\//, "/")
|
39
|
+
end
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_rets_response_received?
|
44
|
+
response_code == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def response_code
|
48
|
+
@rets_response[:ReplyCode].to_i if @rets_response.has_key?(:ReplyCode)
|
49
|
+
end
|
50
|
+
|
51
|
+
def element_tracker_switch element, currently_in
|
52
|
+
@switches[element] = currently_in
|
53
|
+
end
|
54
|
+
|
55
|
+
def switch_active? element
|
56
|
+
(@switches[element] == true)
|
57
|
+
end
|
58
|
+
|
59
|
+
def data_splitter data
|
60
|
+
data.split(/\t/)
|
61
|
+
end
|
62
|
+
|
63
|
+
def data_merger columns, data
|
64
|
+
merged_data = {}
|
65
|
+
columns.each_with_index do |object, index|
|
66
|
+
merged_data[object] = data[index] unless index == 0
|
67
|
+
end
|
68
|
+
merged_data
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module Innards
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
# SAX Parser for RETS Search Response
|
6
|
+
class SearchParser < ParserBase
|
7
|
+
|
8
|
+
attr_reader :columns, :results
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
@results = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_element(name)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def end_element(name)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def attr(name, value)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def text(value)
|
28
|
+
super
|
29
|
+
|
30
|
+
if switch_active?(:COLUMNS)
|
31
|
+
@columns = data_splitter(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
if switch_active?(:DATA)
|
35
|
+
@results.push data_merger(@columns, data_splitter(value))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/innards/version.rb
CHANGED
@@ -0,0 +1,232 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Innards::Connection do
|
4
|
+
|
5
|
+
context "during intialization" do
|
6
|
+
before do
|
7
|
+
@valid_hash = {:login_url => "http://www.dis.com:6103/rets/login"}
|
8
|
+
@invalid_hash = {:login_url => ""}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should validate the URL" do
|
12
|
+
expect { Innards::Connection.new(@valid_hash) }.not_to raise_error()
|
13
|
+
expect { Innards::Connection.new(@invalid_hash) }.to raise_error()
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should valididate RETS Version" do
|
17
|
+
expect { Innards::Connection.new(@valid_hash.merge({:rets_version => "1.5"})) }.not_to raise_error()
|
18
|
+
expect { Innards::Connection.new(@valid_hash.merge({:rets_version => "1.7"})) }.not_to raise_error()
|
19
|
+
expect { Innards::Connection.new(@valid_hash.merge({:rets_version => "1.7.2"})) }.not_to raise_error()
|
20
|
+
expect { Innards::Connection.new(@valid_hash.merge({:rets_version => "1.8"})) }.not_to raise_error()
|
21
|
+
expect { Innards::Connection.new(@valid_hash.merge({:rets_version => "0.9"})) }.to raise_error()
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should validate auth type" do
|
25
|
+
expect { Innards::Connection.new(@valid_hash.merge({:auth_mode => "basic"})) }.not_to raise_error()
|
26
|
+
expect { Innards::Connection.new(@valid_hash.merge({:auth_mode => "digest"})) }.not_to raise_error()
|
27
|
+
expect { Innards::Connection.new(@valid_hash.merge({:auth_mode => "invalid"})) }.to raise_error()
|
28
|
+
end
|
29
|
+
|
30
|
+
it "doesn't accept a blank user agent" do
|
31
|
+
expect { Innards::Connection.new(@valid_hash.merge({:user_agent => ""})) }.to raise_error()
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "during connect" do
|
36
|
+
before do
|
37
|
+
Excon.defaults[:mock] = true
|
38
|
+
stub_path = File.expand_path("../../stubs", __FILE__)
|
39
|
+
Excon.stub({:method => :get}) do |params|
|
40
|
+
rets_version = "RETS/1.7.2"
|
41
|
+
rets_server = "StubRETS/0.0.1"
|
42
|
+
case params[:path]
|
43
|
+
when "/rets/login"
|
44
|
+
{:body => File.read("#{stub_path}/login_success.xml"), :headers => {
|
45
|
+
"RETS-Version" => rets_version,
|
46
|
+
"RETS-Server" => rets_server,
|
47
|
+
"WWW-Authenticate" => "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"",
|
48
|
+
"Set-Cookie" => "name2=value2; Expires=Wed, 09 Jun 2021 10:18:14 GMT"
|
49
|
+
}, :status => 200}
|
50
|
+
when "/rets/getMetadata"
|
51
|
+
{:body => File.read("#{stub_path}/metadata_success.xml"), :headers => {
|
52
|
+
"RETS-Version" => rets_version,
|
53
|
+
"RETS-Server" => rets_server,
|
54
|
+
}, :status => 200}
|
55
|
+
when "/rets/staleDigest"
|
56
|
+
{:body => "", :headers => {
|
57
|
+
"RETS-Version" => rets_version,
|
58
|
+
"RETS-Server" => rets_server,
|
59
|
+
"WWW-Authenticate" => "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\" stale=true",
|
60
|
+
}, :status => 200}
|
61
|
+
when "/rets/retsError"
|
62
|
+
{:body => File.read("#{stub_path}/rets_error.xml"), :headers => {
|
63
|
+
"RETS-Version" => rets_version,
|
64
|
+
"RETS-Server" => rets_server,
|
65
|
+
}, :status => 200}
|
66
|
+
when "/rets/search"
|
67
|
+
{:body => File.read("#{stub_path}/search_success.xml"), :headers => {
|
68
|
+
"RETS-Version" => rets_version,
|
69
|
+
"RETS-Server" => rets_server,
|
70
|
+
}, :status => 200}
|
71
|
+
when "/rets/logout"
|
72
|
+
{:body => File.read("#{stub_path}/logout_success.xml"), :headers => {
|
73
|
+
"RETS-Version" => rets_version,
|
74
|
+
"RETS-Server" => rets_server,
|
75
|
+
}, :status => 200}
|
76
|
+
when "/rets/getObject"
|
77
|
+
{:body => File.read("#{stub_path}/getobject_success.txt"), :headers => {
|
78
|
+
"RETS-Version" => rets_version,
|
79
|
+
"RETS-Server" => rets_server,
|
80
|
+
"Cache-Control" => "private",
|
81
|
+
"MIME-Version" => "1.0",
|
82
|
+
"Content-Type" => "multipart/parallel; boundary=\"aa58ae639983623715598443ccfe159ccf009de8\"",
|
83
|
+
"Transfer-Encoding" => "chunked"
|
84
|
+
}, :status => 200}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
@demo_rets = Innards::Connection.new(:login_url => "http://Joe:Schmoe@www.dis.com:6103/rets/login")
|
88
|
+
end
|
89
|
+
|
90
|
+
after do
|
91
|
+
Excon.stubs.clear
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should throw an exception if get metadata called before login" do
|
95
|
+
expect { @demo_rets.get_metadata }.to raise_error
|
96
|
+
@demo_rets.login!.should be_true
|
97
|
+
expect { @demo_rets.get_metadata }.not_to raise_error
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should login and logout of the demo RETS Server returning associated metadata" do
|
101
|
+
@demo_rets.login!.should be_true
|
102
|
+
@demo_rets.params[:metadata].should be_kind_of(Hash)
|
103
|
+
@demo_rets.params[:metadata].should_not be_empty
|
104
|
+
@demo_rets.params[:metadata][:MemberName].should match "Joe Schmoe"
|
105
|
+
@demo_rets.params[:metadata][:MetadataVersion].should match "1.00.00001"
|
106
|
+
@demo_rets.params[:metadata][:MinMetadataVersion].should match "1.00.00001"
|
107
|
+
@demo_rets.params[:metadata][:User].should match "Joe,NULL,NULL,NULL"
|
108
|
+
@demo_rets.params[:metadata][:Login].should match "/rets/login"
|
109
|
+
@demo_rets.params[:metadata][:Logout].should match "/rets/logout"
|
110
|
+
@demo_rets.params[:metadata][:Search].should match "/rets/search"
|
111
|
+
@demo_rets.params[:metadata][:GetMetadata].should match "/rets/getMetadata"
|
112
|
+
@demo_rets.params[:metadata][:GetObject].should match "/rets/getObject"
|
113
|
+
@demo_rets.params[:metadata][:Balance].should match "116,805.54"
|
114
|
+
@demo_rets.params[:metadata][:TimeoutSeconds].should match "1800"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should throw an error on a stale digest header" do
|
118
|
+
@demo_rets.login!.should be_true
|
119
|
+
@demo_rets.params[:metadata][:GetMetadata] = "/rets/staleDigest"
|
120
|
+
expect { @demo_rets.get_metadata }.to raise_error
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should get the associated metadata for the server" do
|
124
|
+
|
125
|
+
@demo_rets.login!.should be_true
|
126
|
+
metadata = @demo_rets.get_metadata
|
127
|
+
|
128
|
+
metadata.should have_key(:resources)
|
129
|
+
metadata[:resources].should_not be_empty
|
130
|
+
|
131
|
+
metadata.should have_key(:classes)
|
132
|
+
metadata[:classes].should_not be_empty
|
133
|
+
|
134
|
+
metadata.should have_key(:tables)
|
135
|
+
metadata[:tables].should_not be_empty
|
136
|
+
|
137
|
+
metadata.should have_key(:objects)
|
138
|
+
metadata[:objects].should_not be_empty
|
139
|
+
|
140
|
+
metadata.should have_key(:search_help)
|
141
|
+
metadata[:search_help].should_not be_empty
|
142
|
+
|
143
|
+
metadata.should have_key(:edit_masks)
|
144
|
+
metadata[:edit_masks].should_not be_empty
|
145
|
+
|
146
|
+
metadata.should have_key(:lookups)
|
147
|
+
metadata[:lookups].should_not be_empty
|
148
|
+
|
149
|
+
metadata.should have_key(:lookup_types)
|
150
|
+
metadata[:lookup_types].should_not be_empty
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should return false on get metadata error" do
|
155
|
+
@demo_rets.login!.should be_true
|
156
|
+
@demo_rets.params[:metadata][:GetMetadata] = "/rets/retsError"
|
157
|
+
@demo_rets.get_metadata.should be_false
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should throw an error if logout called before login" do
|
161
|
+
expect { @demo_rets.logout! }.to raise_error
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should correctly log out" do
|
165
|
+
@demo_rets.login!.should be_true
|
166
|
+
expect { @demo_rets.logout! }.not_to raise_error
|
167
|
+
@demo_rets.params[:metadata][:ConnectTime].should match "6"
|
168
|
+
@demo_rets.params[:metadata][:Billing].should match "0.04"
|
169
|
+
@demo_rets.params[:metadata][:SignOffMessage].should match "Goodbye"
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should return false on logout error" do
|
173
|
+
@demo_rets.login!.should be_true
|
174
|
+
@demo_rets.params[:metadata][:Logout] = "/rets/retsError"
|
175
|
+
@demo_rets.logout!.should be_false
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should perform a basic search" do
|
179
|
+
@demo_rets.login!.should be_true
|
180
|
+
|
181
|
+
expect {
|
182
|
+
@demo_rets.search()
|
183
|
+
}.to raise_error
|
184
|
+
|
185
|
+
expect {
|
186
|
+
@demo_rets.search(:SearchType => "Property",
|
187
|
+
:Class => "",
|
188
|
+
:Query => "(ListPrice=0+)")
|
189
|
+
}.to raise_error
|
190
|
+
|
191
|
+
expect {
|
192
|
+
@demo_rets.search(:SearchType => "Property",
|
193
|
+
:Class => "RES",
|
194
|
+
:Query => "")
|
195
|
+
}.to raise_error
|
196
|
+
|
197
|
+
expect {
|
198
|
+
@demo_rets.search(:SearchType => "Property",
|
199
|
+
:Class => "RES",
|
200
|
+
:Query => "(ListPrice=0+)")
|
201
|
+
}.not_to raise_error
|
202
|
+
|
203
|
+
results = @demo_rets.search(:SearchType => "Property",
|
204
|
+
:Class => "RES",
|
205
|
+
:Query => "(ListPrice=0+)")
|
206
|
+
results.should be_kind_of(Array)
|
207
|
+
results.each do |result|
|
208
|
+
result.should be_kind_of(Hash)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should return false on search error" do
|
213
|
+
@demo_rets.login!.should be_true
|
214
|
+
@demo_rets.params[:metadata][:Search] = "/rets/retsError"
|
215
|
+
@demo_rets.search(:SearchType => "Property",
|
216
|
+
:Class => "RES",
|
217
|
+
:Query => "(ListPrice=0+)").should be_false
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should perform a GetObject request" do
|
221
|
+
@demo_rets.login!.should be_true
|
222
|
+
|
223
|
+
result = @demo_rets.get_object(:Type => "Photo",
|
224
|
+
:Resource => "Property",
|
225
|
+
:ID => "2:*")
|
226
|
+
result.should be_true
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|