Amunds-rpx_now 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ #Needed to load rpx_now when used as Rails plugin
2
+ require 'rpx_now'
@@ -0,0 +1,185 @@
1
+ require 'json'
2
+ require 'rpx_now/contacts_collection'
3
+ require 'rpx_now/user_integration'
4
+ require 'rpx_now/user_proxy'
5
+
6
+ module RPXNow
7
+ extend self
8
+
9
+ HOST = 'rpxnow.com'
10
+ SSL_CERT = File.join(File.dirname(__FILE__), '..', 'certs', 'ssl_cert.pem')
11
+
12
+ attr_accessor :api_key
13
+ attr_accessor :api_version
14
+ self.api_version = 2
15
+
16
+ # retrieve the users data, or return nil when nothing could be read/token was invalid
17
+ # or data was not found
18
+ def user_data(token, *args)
19
+ api_key, version, options = extract_key_version_and_options!(args)
20
+ options = {:token=>token,:apiKey=>api_key}.merge options
21
+
22
+ begin
23
+ data = secure_json_post("/api/v#{version}/auth_info", options)
24
+ rescue ServerError
25
+ return nil if $!.to_s=~/Data not found/
26
+ raise
27
+ end
28
+ if block_given? then yield(data) else read_user_data_from_response(data) end
29
+ end
30
+
31
+ # maps an identifier to an primary-key (e.g. user.id)
32
+ def map(identifier, primary_key, *args)
33
+ api_key, version, options = extract_key_version_and_options!(args)
34
+ options = {:identifier=>identifier,:primaryKey=>primary_key,:apiKey=>api_key}.merge options
35
+ secure_json_post("/api/v#{version}/map", options)
36
+ end
37
+
38
+ # un-maps an identifier to an primary-key (e.g. user.id)
39
+ def unmap(identifier, primary_key, *args)
40
+ api_key, version, options = extract_key_version_and_options!(args)
41
+ options = {:identifier=>identifier,:primaryKey=>primary_key,:apiKey=>api_key}.merge options
42
+ secure_json_post("/api/v#{version}/unmap", options)
43
+ end
44
+
45
+ # returns an array of identifiers which are mapped to one of your primary-keys (e.g. user.id)
46
+ def mappings(primary_key, *args)
47
+ api_key, version, options = extract_key_version_and_options!(args)
48
+ options = {:primaryKey=>primary_key,:apiKey=>api_key}.merge options
49
+ data = secure_json_post("/api/v#{version}/mappings", options)
50
+ data['identifiers']
51
+ end
52
+
53
+ def all_mappings(*args)
54
+ api_key, version, options = extract_key_version_and_options!(args)
55
+ data = secure_json_post("/api/v#{version}/all_mappings", {:apiKey => api_key}.merge(options))
56
+ data['mappings']
57
+ end
58
+
59
+ def contacts(identifier, *args)
60
+ api_key, version, options = extract_key_version_and_options!(args)
61
+ options = {:apiKey => api_key, :identifier=> identifier}.merge(options)
62
+ data = secure_json_post("/api/v#{version}/get_contacts", options)
63
+ RPXNow::ContactsCollection.new(data['response'])
64
+ end
65
+ alias get_contacts contacts
66
+
67
+ def embed_code(subdomain,url,options={})
68
+ options = {:width => '400', :height => '240', :language => 'en'}.merge(options)
69
+ <<EOF
70
+ <iframe src="https://#{subdomain}.#{HOST}/openid/embed?token_url=#{url}&language_preference=#{options[:language]}"
71
+ scrolling="no" frameBorder="no" style="width:#{options[:width]}px;height:#{options[:height]}px;">
72
+ </iframe>
73
+ EOF
74
+ end
75
+
76
+ def popup_code(text, subdomain, url, options = {})
77
+ if options[:unobtrusive]
78
+ unobtrusive_popup_code(text, subdomain, url, options)
79
+ else
80
+ obtrusive_popup_code(text, subdomain, url, options)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def unobtrusive_popup_code(text, subdomain, url, options={})
87
+ version = extract_version! options
88
+ "<a class=\"rpxnow\" href=\"https://#{subdomain}.#{HOST}/openid/v#{version}/signin?token_url=#{url}\">#{text}</a>"
89
+ end
90
+
91
+ def obtrusive_popup_code(text, subdomain, url, options = {})
92
+ version = extract_version! options
93
+ <<EOF
94
+ <a class="rpxnow" onclick="return false;" href="https://#{subdomain}.#{HOST}/openid/v#{version}/signin?token_url=#{url}">
95
+ #{text}
96
+ </a>
97
+ <script src="https://#{HOST}/openid/v#{version}/widget" type="text/javascript"></script>
98
+ <script type="text/javascript">
99
+ //<![CDATA[
100
+ RPXNOW.token_url = "#{url}";
101
+
102
+ RPXNOW.realm = "#{subdomain}";
103
+ RPXNOW.overlay = true;
104
+ RPXNOW.language_preference = '#{options[:language]||'en'}';
105
+ //]]>
106
+ </script>
107
+ EOF
108
+ end
109
+
110
+ def extract_key_version_and_options!(args)
111
+ key, options = extract_key_and_options(args)
112
+ version = extract_version! options
113
+ [key, version, options]
114
+ end
115
+
116
+ # [API_KEY,{options}] or
117
+ # [{options}] or
118
+ # []
119
+ def extract_key_and_options(args)
120
+ if args.length == 2
121
+ [args[0],args[1]]
122
+ elsif args.length==1
123
+ if args[0].is_a? Hash then [@api_key,args[0]] else [args[0],{}] end
124
+ else
125
+ raise "NO Api Key found!" unless @api_key
126
+ [@api_key,{}]
127
+ end
128
+ end
129
+
130
+ def extract_version!(options)
131
+ options.delete(:api_version) || api_version
132
+ end
133
+
134
+ def read_user_data_from_response(response)
135
+ user_data = response['profile']
136
+ data = {}
137
+ data[:identifier] = user_data['identifier']
138
+ data[:email] = user_data['verifiedEmail'] || user_data['email']
139
+ data[:username] = user_data['preferredUsername'] || data[:email].to_s.sub(/@.*/,'')
140
+ data[:name] = user_data['displayName'] || data[:username]
141
+ data[:id] = user_data['primaryKey'] unless user_data['primaryKey'].to_s.empty?
142
+ data
143
+ end
144
+
145
+ def secure_json_post(path, data)
146
+ parse_response(post(path,data))
147
+ end
148
+
149
+ def post(path, data)
150
+ require 'net/http'
151
+ require 'net/https'
152
+ request = Net::HTTP::Get.new(path)
153
+ request.form_data = data.map{|k,v| [k.to_s,v]}#symbol keys -> string because of ruby 1.9.x bug http://redmine.ruby-lang.org/issues/show/1351
154
+ make_request(request)
155
+ end
156
+
157
+ def make_request(request)
158
+ http = Net::HTTP.new(HOST, 443)
159
+ http.use_ssl = true
160
+ http.ca_file = SSL_CERT
161
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
162
+ http.verify_depth = 5
163
+ http.request(request)
164
+ end
165
+
166
+ def parse_response(response)
167
+ if response.code.to_i >= 400
168
+ raise ServiceUnavailableError, "The RPX service is temporarily unavailable. (4XX)"
169
+ else
170
+ result = JSON.parse(response.body)
171
+ return result unless result['err']
172
+
173
+ code = result['err']['code']
174
+ if code == -1
175
+ raise ServiceUnavailableError, "The RPX service is temporarily unavailable."
176
+ else
177
+ raise ApiError, "Got error: #{result['err']['msg']} (code: #{code}), HTTP status: #{response.code}"
178
+ end
179
+ end
180
+ end
181
+
182
+ class ServerError < Exception; end #backwards compatibility / catch all
183
+ class ApiError < ServerError; end
184
+ class ServiceUnavailableError < ServerError; end
185
+ end
@@ -0,0 +1,19 @@
1
+ module RPXNow
2
+ class ContactsCollection < Array
3
+ def initialize(list)
4
+ @raw = list
5
+ @additional_info = list.reject{|k,v|k=='entry'}
6
+ list['entry'].each{|item| self << parse_data(item)}
7
+ end
8
+
9
+ def additional_info;@additional_info;end
10
+ def raw;@raw;end
11
+
12
+ private
13
+
14
+ def parse_data(entry)
15
+ entry['emails'] = entry['emails'].map{|email| email['value']}
16
+ entry
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module RPXNow
2
+ module UserIntegration
3
+ def rpx
4
+ RPXNow::UserProxy.new(id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module RPXNow
2
+ class UserProxy
3
+ def initialize(id)
4
+ @id = id
5
+ end
6
+
7
+ def identifiers
8
+ RPXNow.mappings(@id)
9
+ end
10
+
11
+ def map(identifier)
12
+ RPXNow.map(identifier, @id)
13
+ end
14
+
15
+ def unmap(identifier)
16
+ RPXNow.unmap(identifier, @id)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,57 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rpx_now}
5
+ s.version = "0.5.8"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Michael Grosser"]
9
+ s.date = %q{2009-06-21}
10
+ s.email = %q{grosser.michael@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "README.markdown"
13
+ ]
14
+ s.files = [
15
+ "CHANGELOG",
16
+ "MIGRATION",
17
+ "README.markdown",
18
+ "Rakefile",
19
+ "VERSION.yml",
20
+ "certs/ssl_cert.pem",
21
+ "init.rb",
22
+ "lib/rpx_now.rb",
23
+ "lib/rpx_now/contacts_collection.rb",
24
+ "lib/rpx_now/user_integration.rb",
25
+ "lib/rpx_now/user_proxy.rb",
26
+ "rpx_now.gemspec",
27
+ "spec/fixtures/get_contacts_response.json",
28
+ "spec/rpx_now/contacts_collection_spec.rb",
29
+ "spec/rpx_now/user_proxy_spec.rb",
30
+ "spec/rpx_now_spec.rb",
31
+ "spec/spec_helper.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/grosser/rpx_now}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.4}
37
+ s.summary = %q{Helper to simplify RPX Now user login/creation}
38
+ s.test_files = [
39
+ "spec/rpx_now_spec.rb",
40
+ "spec/rpx_now/contacts_collection_spec.rb",
41
+ "spec/rpx_now/user_proxy_spec.rb",
42
+ "spec/spec_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
51
+ else
52
+ s.add_dependency(%q<activesupport>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<activesupport>, [">= 0"])
56
+ end
57
+ end
@@ -0,0 +1,58 @@
1
+ {
2
+ "response": {
3
+ "itemsPerPage": 5,
4
+ "totalResults": 5,
5
+ "entry": [
6
+ {
7
+ "displayName": "Bob Johnson",
8
+ "emails": [
9
+ {
10
+ "type": "other",
11
+ "value": "bob@example.com"
12
+ }
13
+ ]
14
+ },
15
+ {
16
+ "displayName": "Cindy Smith",
17
+ "emails": [
18
+ {
19
+ "type": "other",
20
+ "value": "cindy.smith@example.com"
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "displayName": "Fred Williams",
26
+ "emails": [
27
+ {
28
+ "type": "other",
29
+ "value": "fred.williams@example.com"
30
+ },
31
+ {
32
+ "type": "other",
33
+ "value": "fred@example.com"
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "emails": [
39
+ {
40
+ "type": "other",
41
+ "value": "j.lewis@example.com"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "displayName": "Mary Jones",
47
+ "emails": [
48
+ {
49
+ "type": "other",
50
+ "value": "mary.jones@example.com"
51
+ }
52
+ ]
53
+ }
54
+ ],
55
+ "startIndex": 1
56
+ },
57
+ "stat": "ok"
58
+ }
@@ -0,0 +1,32 @@
1
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
2
+
3
+ describe RPXNow::ContactsCollection do
4
+ before do
5
+ data = JSON.parse(File.read('spec/fixtures/get_contacts_response.json'))['response']
6
+ @collection = RPXNow::ContactsCollection.new(data)
7
+ end
8
+
9
+ it "behaves like an array" do
10
+ @collection.size.should == 5
11
+ @collection[0] = "1"
12
+ @collection[0].should == "1"
13
+ end
14
+
15
+ it "parses entry to items" do
16
+ @collection[0]['displayName'].should == "Bob Johnson"
17
+ end
18
+
19
+ it "parses emails to list" do
20
+ @collection[0]['emails'].should == ["bob@example.com"]
21
+ end
22
+
23
+ it "parses emails to list with multiple emails" do
24
+ @collection[2]['emails'].should == ["fred.williams@example.com","fred@example.com"]
25
+ end
26
+
27
+ it "holds additional_info" do
28
+ @collection.additional_info['startIndex'].should == 1
29
+ @collection.additional_info['itemsPerPage'].should == 5
30
+ @collection.additional_info['totalResults'].should == 5
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
2
+
3
+ class User
4
+ include RPXNow::UserIntegration
5
+
6
+ def id
7
+ 5
8
+ end
9
+ end
10
+
11
+ describe RPXNow::UserProxy do
12
+ before do
13
+ RPXNow.unmap('http://test.myopenid.com', 5)
14
+ end
15
+
16
+ it "has identifiers" do
17
+ RPXNow.map('http://test.myopenid.com', 5)
18
+ User.new.rpx.identifiers.should == ['http://test.myopenid.com']
19
+ end
20
+
21
+ it "can map" do
22
+ User.new.rpx.map('http://test.myopenid.com')
23
+ User.new.rpx.identifiers.should == ['http://test.myopenid.com']
24
+ end
25
+
26
+ it "can unmap" do
27
+ RPXNow.map('http://test.myopenid.com', 5)
28
+ User.new.rpx.unmap('http://test.myopenid.com')
29
+ User.new.rpx.identifiers.should == []
30
+ end
31
+ end
@@ -0,0 +1,263 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ describe RPXNow do
4
+ describe :api_key= do
5
+ it "stores the api key, so i do not have to supply everytime" do
6
+ RPXNow.api_key='XX'
7
+ RPXNow.expects(:post).with{|x,data|data[:apiKey]=='XX'}.returns mock(:code=>'200', :body=>%Q({"stat":"ok"}))
8
+ RPXNow.mappings(1)
9
+ end
10
+ end
11
+
12
+ describe :api_version= do
13
+ it "can be set to a api_version globally" do
14
+ RPXNow.api_version = 5
15
+ RPXNow.popup_code('x','y','z').should =~ %r(/openid/v5/signin)
16
+ end
17
+ end
18
+
19
+ describe :embed_code do
20
+ it "contains the subdomain" do
21
+ RPXNow.embed_code('xxx','my_url').should =~ /xxx/
22
+ end
23
+
24
+ it "contains the url" do
25
+ RPXNow.embed_code('xxx','my_url').should =~ /token_url=my_url/
26
+ end
27
+
28
+ it "defaults to English" do
29
+ RPXNow.embed_code('xxx', 'my_url').should =~ /language_preference=en/
30
+ end
31
+
32
+ it "has a changeable language" do
33
+ RPXNow.embed_code('xxx', 'my_url', :language => 'es').should =~ /language_preference=es/
34
+ end
35
+
36
+ it "defaults to 400px width" do
37
+ RPXNow.embed_code('xxx', 'my_url').should =~ /width:400px;/
38
+ end
39
+
40
+ it "has a changeable width" do
41
+ RPXNow.embed_code('xxx', 'my_url', :width => '300').should =~ /width:300px;/
42
+ end
43
+
44
+ it "defaults to 240px height" do
45
+ RPXNow.embed_code('xxx', 'my_url').should =~ /height:240px;/
46
+ end
47
+
48
+ it "has a changeable height" do
49
+ RPXNow.embed_code('xxx', 'my_url', :height => '500').should =~ /height:500px;/
50
+ end
51
+ end
52
+
53
+ describe :popup_code do
54
+ it "defaults to obtrusive output" do
55
+ RPXNow.popup_code('sign on', 'subdomain', 'http://fake.domain.com/').should =~ /script src=/
56
+ end
57
+
58
+ it "can build an unobtrusive widget with specific version" do
59
+ expected = %Q(<a class="rpxnow" href="https://subdomain.rpxnow.com/openid/v300/signin?token_url=http://fake.domain.com/">sign on</a>)
60
+ RPXNow.popup_code('sign on', 'subdomain', 'http://fake.domain.com/', { :unobtrusive => true, :api_version => 300 }).should == expected
61
+ end
62
+
63
+ it "allows to specify the version of the widget" do
64
+ RPXNow.popup_code('x','y','z', :api_version => 300).should =~ %r(/openid/v300/signin)
65
+ end
66
+
67
+ it "defaults to widget version 2" do
68
+ RPXNow.popup_code('x','y','z').should =~ %r(/openid/v2/signin)
69
+ end
70
+
71
+ it "defaults to english" do
72
+ RPXNow.popup_code('x','y','z').should =~ /RPXNOW.language_preference = 'en'/
73
+ end
74
+
75
+ it "has a changeable language" do
76
+ RPXNow.popup_code('x','y','z',:language=>'de').should =~ /RPXNOW.language_preference = 'de'/
77
+ end
78
+ end
79
+
80
+ describe :user_data do
81
+ before do
82
+ @response_body = %Q({"profile":{"verifiedEmail":"grosser.michael@googlemail.com","displayName":"Michael Grosser","preferredUsername":"grosser.michael","identifier":"https:\/\/www.google.com\/accounts\/o8\/id?id=AItOawmaOlyYezg_WfbgP_qjaUyHjmqZD9qNIVM","email":"grosser.michael@gmail.com"},"stat":"ok"})
83
+ @fake_user_data = {'profile'=>{}}
84
+ end
85
+
86
+ def fake_response
87
+ mock(:code=>"200",:body=>@response_body)
88
+ end
89
+
90
+ it "raises ApiError when used with an invalid token" do
91
+ lambda{
92
+ RPXNow.user_data('xxxx')
93
+ }.should raise_error(RPXNow::ApiError)
94
+ end
95
+
96
+ it "is empty when used with an unknown token" do
97
+ RPXNow.user_data('60d8c6374f4e9d290a7b55f39da7cc6435aef3d3').should == nil
98
+ end
99
+
100
+ it "parses JSON response to user data" do
101
+ RPXNow.expects(:post).returns fake_response
102
+ RPXNow.user_data('').should == {:name=>'Michael Grosser',:email=>'grosser.michael@googlemail.com',:identifier=>"https://www.google.com/accounts/o8/id?id=AItOawmaOlyYezg_WfbgP_qjaUyHjmqZD9qNIVM", :username => 'grosser.michael'}
103
+ end
104
+
105
+ it "adds a :id when primaryKey was returned" do
106
+ @response_body.sub!(%Q("verifiedEmail"), %Q("primaryKey":"2","verifiedEmail"))
107
+ RPXNow.expects(:post).returns fake_response
108
+ RPXNow.user_data('')[:id].should == '2'
109
+ end
110
+
111
+ it "handles primaryKeys that are not numeric" do
112
+ @response_body.sub!(%Q("verifiedEmail"), %Q("primaryKey":"dbalatero","verifiedEmail"))
113
+ RPXNow.expects(:post).returns fake_response
114
+ RPXNow.user_data('')[:id].should == 'dbalatero'
115
+ end
116
+
117
+ it "hands JSON response to supplied block" do
118
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"x":"1","stat":"ok"}))
119
+ response = nil
120
+ RPXNow.user_data(''){|data| response = data}
121
+ response.should == {"x" => "1", "stat" => "ok"}
122
+ end
123
+
124
+ it "returns what the supplied block returned" do
125
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"x":"1","stat":"ok"}))
126
+ RPXNow.user_data(''){|data| "x"}.should == 'x'
127
+ end
128
+
129
+ it "can send additional parameters" do
130
+ RPXNow.expects(:post).with{|url,data|
131
+ data[:extended].should == 'true'
132
+ }.returns fake_response
133
+ RPXNow.user_data('',:extended=>'true')
134
+ end
135
+
136
+ it "works with api key as 2nd parameter (backwards compatibility)" do
137
+ RPXNow.expects(:secure_json_post).with('/api/v2/auth_info', :apiKey=>'THE KEY', :token=>'id').returns @fake_user_data
138
+ RPXNow.user_data('id', 'THE KEY')
139
+ RPXNow.api_key.should == API_KEY
140
+ end
141
+
142
+ it "works with api key as 2nd parameter and options (backwards compatibility)" do
143
+ RPXNow.expects(:secure_json_post).with('/api/v2/auth_info', :apiKey=>'THE KEY', :extended=>'abc', :token=>'id' ).returns @fake_user_data
144
+ RPXNow.user_data('id', 'THE KEY', :extended=>'abc')
145
+ RPXNow.api_key.should == API_KEY
146
+ end
147
+
148
+ it "works with api version as option (backwards compatibility)" do
149
+ RPXNow.expects(:secure_json_post).with('/api/v123/auth_info', :apiKey=>API_KEY, :token=>'id', :extended=>'abc').returns @fake_user_data
150
+ RPXNow.user_data('id', :extended=>'abc', :api_version=>123)
151
+ RPXNow.api_version.should == API_VERSION
152
+ end
153
+ end
154
+
155
+ describe :read_user_data_from_response do
156
+ it "reads secondary names" do
157
+ RPXNow.send(:read_user_data_from_response,{'profile'=>{'preferredUsername'=>'1'}})[:name].should == '1'
158
+ end
159
+
160
+ it "parses email when no name is found" do
161
+ RPXNow.send(:read_user_data_from_response,{'profile'=>{'email'=>'1@xxx.com'}})[:name].should == '1'
162
+ end
163
+ end
164
+
165
+ describe :contacts do
166
+ it "finds all contacts" do
167
+ response = JSON.parse(File.read('spec/fixtures/get_contacts_response.json'))
168
+ RPXNow.expects(:secure_json_post).with('/api/v2/get_contacts',:identifier=>'xx', :apiKey=>API_KEY).returns response
169
+ RPXNow.contacts('xx').size.should == 5
170
+ end
171
+ end
172
+
173
+ describe :parse_response do
174
+ it "parses json when status is ok" do
175
+ response = mock(:code=>'200', :body=>%Q({"stat":"ok","data":"xx"}))
176
+ RPXNow.send(:parse_response, response)['data'].should == "xx"
177
+ end
178
+
179
+ it "raises when there is a communication error" do
180
+ response = stub(:code=>'200', :body=>%Q({"err":"wtf","stat":"ok"}))
181
+ lambda{
182
+ RPXNow.send(:parse_response,response)
183
+ }.should raise_error(RPXNow::ApiError)
184
+ end
185
+
186
+ it "raises when service has downtime" do
187
+ response = stub(:code=>'200', :body=>%Q({"err":{"code":-1},"stat":"ok"}))
188
+ lambda{
189
+ RPXNow.send(:parse_response,response)
190
+ }.should raise_error(RPXNow::ServiceUnavailableError)
191
+ end
192
+
193
+ it "raises when service is down" do
194
+ response = stub(:code=>'400',:body=>%Q({"stat":"err"}))
195
+ lambda{
196
+ RPXNow.send(:parse_response,response)
197
+ }.should raise_error(RPXNow::ServiceUnavailableError)
198
+ end
199
+ end
200
+
201
+ describe :mappings do
202
+ it "parses JSON response to unmap data" do
203
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"stat":"ok", "identifiers": ["http://test.myopenid.com/"]}))
204
+ RPXNow.mappings(1, "x").should == ["http://test.myopenid.com/"]
205
+ end
206
+ end
207
+
208
+ describe :map do
209
+ it "adds a mapping" do
210
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"stat":"ok"}))
211
+ RPXNow.map('http://test.myopenid.com',1, API_KEY)
212
+ end
213
+ end
214
+
215
+ describe :unmap do
216
+ it "unmaps a indentifier" do
217
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"stat":"ok"}))
218
+ RPXNow.unmap('http://test.myopenid.com', 1, "x")
219
+ end
220
+
221
+ it "can be called with a specific version" do
222
+ RPXNow.expects(:secure_json_post).with{|a,b|a == "/api/v300/unmap"}
223
+ RPXNow.unmap('http://test.myopenid.com', 1, :api_key=>'xxx', :api_version=>300)
224
+ end
225
+ end
226
+
227
+ describe :mapping_integration do
228
+ before do
229
+ @k1 = 'http://test.myopenid.com'
230
+ RPXNow.unmap(@k1, 1)
231
+ @k2 = 'http://test-2.myopenid.com'
232
+ RPXNow.unmap(@k2, 1)
233
+ end
234
+
235
+ it "has no mappings when nothing was mapped" do
236
+ RPXNow.mappings(1).should == []
237
+ end
238
+
239
+ it "unmaps mapped keys" do
240
+ RPXNow.map(@k2, 1)
241
+ RPXNow.unmap(@k2, 1)
242
+ RPXNow.mappings(1).should == []
243
+ end
244
+
245
+ it "maps keys to a primary key and then retrieves them" do
246
+ RPXNow.map(@k1, 1)
247
+ RPXNow.map(@k2, 1)
248
+ RPXNow.mappings(1).sort.should == [@k2,@k1]
249
+ end
250
+
251
+ it "does not add duplicate mappings" do
252
+ RPXNow.map(@k1, 1)
253
+ RPXNow.map(@k1, 1)
254
+ RPXNow.mappings(1).should == [@k1]
255
+ end
256
+
257
+ it "finds all mappings" do
258
+ RPXNow.map(@k1, 1)
259
+ RPXNow.map(@k2, 2)
260
+ RPXNow.all_mappings.sort.should == [["1", ["http://test.myopenid.com"]], ["2", ["http://test-2.myopenid.com"]]]
261
+ end
262
+ end
263
+ end