uservoice-ruby 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,132 @@
1
+ module UserVoice
2
+ class Client
3
+
4
+ def initialize(*args)
5
+ case args.size
6
+ when 3,4
7
+ init_subdomain_and_api_keys(*args)
8
+ when 1,2
9
+ init_consumer_and_access_token(*args)
10
+ end
11
+ end
12
+
13
+ def init_subdomain_and_api_keys(subdomain_name, api_key, api_secret, attrs={})
14
+ consumer = OAuth::Consumer.new(api_key, api_secret, {
15
+ :site => "#{attrs[:protocol] || 'https'}://#{subdomain_name}.#{attrs[:uservoice_domain] || 'uservoice.com'}"
16
+ })
17
+ init_consumer_and_access_token(consumer, attrs)
18
+ end
19
+
20
+ def init_consumer_and_access_token(consumer, attrs={})
21
+ @consumer = consumer
22
+ @token = OAuth::AccessToken.new(@consumer, attrs[:oauth_token] || '', attrs[:oauth_token_secret] || '')
23
+ @response_format = attrs[:response_format] || :hash
24
+ @callback = attrs[:callback]
25
+ end
26
+
27
+ def authorize_url
28
+ request_token.authorize_url
29
+ end
30
+
31
+ def login_with_verifier(oauth_verifier)
32
+ raise Unauthorized.new('Call request token first') if @request_token.nil?
33
+ token = @request_token.get_access_token(:oauth_verifier => oauth_verifier)
34
+ Client.new(@consumer, :oauth_token => token.token, :oauth_token_secret => token.secret)
35
+ end
36
+
37
+ def login_with_access_token(oauth_token, oauth_token_secret, &block)
38
+ token = Client.new(@consumer, :oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret)
39
+ if block_given?
40
+ yield token
41
+ else
42
+ return token
43
+ end
44
+ end
45
+
46
+ def token
47
+ @token.token
48
+ end
49
+
50
+ def secret
51
+ @token.secret
52
+ end
53
+
54
+ def request_token
55
+ @request_token = @consumer.get_request_token(:oauth_callback => @callback)
56
+ end
57
+
58
+ def login_as_owner(&block)
59
+ token = post('/api/v1/users/login_as_owner.json', {
60
+ 'request_token' => request_token.token
61
+ })['token']
62
+ if token
63
+ login_with_access_token(token['oauth_token'], token['oauth_token_secret'], &block)
64
+ else
65
+ raise Unauthorized.new("Could not get Access Token")
66
+ end
67
+ end
68
+
69
+ def login_as(email, &block)
70
+ unless email.to_s.match(EMAIL_FORMAT)
71
+ raise Unauthorized.new("'#{email}' is not a valid email address")
72
+ end
73
+ token = post('/api/v1/users/login_as.json', {
74
+ :user => { :email => email },
75
+ :request_token => request_token.token
76
+ })['token']
77
+
78
+ if token
79
+ login_with_access_token(token['oauth_token'], token['oauth_token_secret'], &block)
80
+ else
81
+ raise Unauthorized.new("Could not get Access Token")
82
+ end
83
+ end
84
+
85
+ def request(method, uri, request_body={}, headers={})
86
+ headers = DEFAULT_HEADERS.merge(headers)
87
+
88
+ if headers['Content-Type'] == 'application/json' && request_body.is_a?(Hash)
89
+ request_body = request_body.to_json
90
+ end
91
+
92
+ response = case method.to_sym
93
+ when :post, :put
94
+ @token.request(method, uri, request_body, headers)
95
+ when :head, :delete, :get
96
+ @token.request(method, uri, headers)
97
+ else
98
+ raise RuntimeError.new("Invalid HTTP method #{method}")
99
+ end
100
+
101
+ return case @response_format.to_s
102
+ when 'raw'
103
+ response
104
+ else
105
+ attrs = JSON.parse(response.body)
106
+ if attrs && attrs['errors']
107
+ case attrs['errors']['type']
108
+ when 'unauthorized'
109
+ raise Unauthorized.new(attrs)
110
+ when 'record_not_found'
111
+ raise NotFound.new(attrs)
112
+ when 'application_error'
113
+ raise ApplicationError.new(attrs)
114
+ else
115
+ raise APIError.new(attrs)
116
+ end
117
+ end
118
+ attrs
119
+ end
120
+ end
121
+
122
+ %w(get post delete put).each do |method|
123
+ define_method(method) do |*args|
124
+ request(method, *args)
125
+ end
126
+ end
127
+
128
+ def get_collection(uri, opts={})
129
+ UserVoice::Collection.new(self, uri, opts)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,63 @@
1
+ module UserVoice
2
+ class Collection
3
+ def initialize(client, query, opts={})
4
+ @client = client
5
+ @query = query
6
+ @limit = opts[:limit] || 2**60
7
+ @per_page = [@limit, 500].min
8
+ @pages = {}
9
+ end
10
+
11
+ def first
12
+ load_record(0)
13
+ end
14
+
15
+ def last
16
+ load_record(size() - 1)
17
+ end
18
+
19
+ def size
20
+ if @response_data.nil?
21
+ load_record(0)
22
+ end
23
+ @response_data['total_records']
24
+ end
25
+
26
+ def map
27
+ index = 0
28
+ records = []
29
+ while record = load_record(index)
30
+ records.push(yield record)
31
+ index += 1
32
+ end
33
+ return records
34
+ end
35
+ alias collect map
36
+
37
+ def each
38
+ map do |value|
39
+ yield value
40
+ value
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def load_record(i)
47
+ load_page((i/500.0).floor + 1)[i%500]
48
+ end
49
+
50
+ def load_page(i)
51
+ if @pages[i].nil?
52
+ result = @client.get("#{@query}#{@query.include?('?') ? '&' : '?'}per_page=#{@per_page}&page=#{i}")
53
+
54
+ if @response_data = result.delete('response_data')
55
+ @pages[i] = result.shift.last if result.first
56
+ else
57
+ raise UserVoice::NotFound.new('The resource you requested is not a collection')
58
+ end
59
+ end
60
+ return @pages[i]
61
+ end
62
+ end
63
+ end
@@ -1,4 +1,6 @@
1
1
  require "uservoice/version"
2
+ require 'uservoice/collection'
3
+ require 'uservoice/client'
2
4
  require 'rubygems'
3
5
  require 'ezcrypto'
4
6
  require 'json'
@@ -31,131 +33,4 @@ module UserVoice
31
33
 
32
34
  return CGI.escape(encoded)
33
35
  end
34
-
35
- class Client
36
-
37
- def initialize(*args)
38
- case args.size
39
- when 3,4
40
- init_subdomain_and_api_keys(*args)
41
- when 1,2
42
- init_consumer_and_access_token(*args)
43
- end
44
- end
45
-
46
- def init_subdomain_and_api_keys(subdomain_name, api_key, api_secret, attrs={})
47
- consumer = OAuth::Consumer.new(api_key, api_secret, {
48
- :site => "#{attrs[:protocol] || 'https'}://#{subdomain_name}.#{attrs[:uservoice_domain] || 'uservoice.com'}"
49
- })
50
- init_consumer_and_access_token(consumer, attrs)
51
- end
52
-
53
- def init_consumer_and_access_token(consumer, attrs={})
54
- @consumer = consumer
55
- @token = OAuth::AccessToken.new(@consumer, attrs[:oauth_token] || '', attrs[:oauth_token_secret] || '')
56
- @response_format = attrs[:response_format] || :hash
57
- @callback = attrs[:callback]
58
- end
59
-
60
- def authorize_url
61
- request_token.authorize_url
62
- end
63
-
64
- def login_with_verifier(oauth_verifier)
65
- raise Unauthorized.new('Call request token first') if @request_token.nil?
66
- token = @request_token.get_access_token(:oauth_verifier => oauth_verifier)
67
- Client.new(@consumer, :oauth_token => token.token, :oauth_token_secret => token.secret)
68
- end
69
-
70
- def login_with_access_token(oauth_token, oauth_token_secret, &block)
71
- token = Client.new(@consumer, :oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret)
72
- if block_given?
73
- yield token
74
- else
75
- return token
76
- end
77
- end
78
-
79
- def token
80
- @token.token
81
- end
82
-
83
- def secret
84
- @token.secret
85
- end
86
-
87
- def request_token
88
- @request_token = @consumer.get_request_token(:oauth_callback => @callback)
89
- end
90
-
91
- def login_as_owner(&block)
92
- token = post('/api/v1/users/login_as_owner.json', {
93
- 'request_token' => request_token.token
94
- })['token']
95
- if token
96
- login_with_access_token(token['oauth_token'], token['oauth_token_secret'], &block)
97
- else
98
- raise Unauthorized.new("Could not get Access Token")
99
- end
100
- end
101
-
102
- def login_as(email, &block)
103
- unless email.to_s.match(EMAIL_FORMAT)
104
- raise Unauthorized.new("'#{email}' is not a valid email address")
105
- end
106
- token = post('/api/v1/users/login_as.json', {
107
- :user => { :email => email },
108
- :request_token => request_token.token
109
- })['token']
110
-
111
- if token
112
- login_with_access_token(token['oauth_token'], token['oauth_token_secret'], &block)
113
- else
114
- raise Unauthorized.new("Could not get Access Token")
115
- end
116
- end
117
-
118
- def request(method, uri, request_body={}, headers={})
119
- headers = DEFAULT_HEADERS.merge(headers)
120
-
121
- if headers['Content-Type'] == 'application/json' && request_body.is_a?(Hash)
122
- request_body = request_body.to_json
123
- end
124
-
125
- response = case method.to_sym
126
- when :post, :put
127
- @token.request(method, uri, request_body, headers)
128
- when :head, :delete, :get
129
- @token.request(method, uri, headers)
130
- else
131
- raise RuntimeError.new("Invalid HTTP method #{method}")
132
- end
133
-
134
- return case @response_format.to_s
135
- when 'raw'
136
- response
137
- else
138
- attrs = JSON.parse(response.body)
139
- if attrs && attrs['errors']
140
- case attrs['errors']['type']
141
- when 'unauthorized'
142
- raise Unauthorized.new(attrs)
143
- when 'record_not_found'
144
- raise NotFound.new(attrs)
145
- when 'application_error'
146
- raise ApplicationError.new(attrs)
147
- else
148
- raise APIError.new(attrs)
149
- end
150
- end
151
- attrs
152
- end
153
- end
154
-
155
- %w(get post delete put).each do |method|
156
- define_method(method) do |*args|
157
- request(method, *args)
158
- end
159
- end
160
- end
161
36
  end
@@ -1,3 +1,3 @@
1
1
  module Uservoice
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -12,175 +12,4 @@ describe UserVoice do
12
12
  key = EzCrypto::Key.with_password(config['subdomain_name'], config['sso_key'])
13
13
  key.decrypt(encrypted_raw_data).should match('mailaddress@example.com')
14
14
  end
15
-
16
- describe UserVoice::Client do
17
- subject { UserVoice::Client.new(config['subdomain_name'],
18
- config['api_key'],
19
- config['api_secret'],
20
- :uservoice_domain => config['uservoice_domain'],
21
- :protocol => config['protocol']) }
22
-
23
- it "should get user names from the API" do
24
- users = subject.get("/api/v1/users.json?per_page=3")
25
- user_names = users['users'].map { |user| user['name'] }
26
- user_names.all?.should == true
27
- user_names.size.should == 3
28
- end
29
-
30
- it "should not get current user without logged in user" do
31
- lambda do
32
- user = subject.get("/api/v1/users/current.json")
33
- end.should raise_error(UserVoice::Unauthorized)
34
- end
35
-
36
- it "should be able to get access token as owner" do
37
- subject.login_as_owner do |owner|
38
- owner.get("/api/v1/users/current.json")['user']['roles']['owner'].should == true
39
-
40
- owner.login_as('regular@example.com') do |regular|
41
- owner.get("/api/v1/users/current.json")['user']['roles']['owner'].should == true
42
- @user = regular.get("/api/v1/users/current.json")['user']
43
- @user['roles']['owner'].should == false
44
- end
45
-
46
- owner.get("/api/v1/users/current.json")['user']['roles']['owner'].should == true
47
- end
48
- # ensure blocks got run
49
- @user['email'].should == 'regular@example.com'
50
- end
51
-
52
- it "should not be able to create KB article as nobody" do
53
- lambda do
54
- result = subject.post("/api/v1/articles.json", :article => {
55
- :title => 'good morning'
56
- })
57
- end.should raise_error(UserVoice::Unauthorized)
58
- end
59
-
60
- it "should be able to create and delete a forum as the owner" do
61
- owner = subject.login_as_owner
62
- forum = owner.post("/api/v1/forums.json", :forum => {
63
- :name => 'Test forum from RSpec',
64
- 'private' => true,
65
- 'allow_by_email_domain' => true,
66
- 'allowed_email_domains' => [{'domain' => 'raimo.rspec.example.com'}]
67
- })['forum']
68
-
69
- forum['id'].should be_a(Integer)
70
-
71
- deleted_forum = owner.delete("/api/v1/forums/#{forum['id']}.json")['forum']
72
- deleted_forum['id'].should == forum['id']
73
- end
74
-
75
- it "should get current user with 2-legged call" do
76
- user = subject.login_as('mailaddress@example.com') do |token|
77
- token.get("/api/v1/users/current.json")['user']
78
- end
79
-
80
- user['email'].should == 'mailaddress@example.com'
81
- end
82
-
83
- it "should get current user with copied access token" do
84
- original_token = subject.login_as('mailaddress@example.com')
85
-
86
- client = UserVoice::Client.new(config['subdomain_name'],
87
- config['api_key'],
88
- config['api_secret'],
89
- :uservoice_domain => config['uservoice_domain'],
90
- :protocol => config['protocol'],
91
- :oauth_token => original_token.token,
92
- :oauth_token_secret => original_token.secret)
93
- # Also this works but creates an extra object:
94
- # client = client.login_with_access_token(original_token.token, original_token.secret)
95
-
96
- user = client.get("/api/v1/users/current.json")['user']
97
-
98
- user['email'].should == 'mailaddress@example.com'
99
- end
100
-
101
- it "should login as an owner" do
102
- me = subject.login_as_owner
103
-
104
- owner = me.get("/api/v1/users/current.json")['user']
105
- owner['roles']['owner'].should == true
106
- end
107
-
108
- it "should not be able to delete when not deleting on behalf of anyone" do
109
- lambda {
110
- result = subject.delete("/api/v1/users/#{234}.json")
111
- }.should raise_error(UserVoice::Unauthorized, /user required/i)
112
- end
113
-
114
- it "should not be able to delete owner" do
115
- owner_access_token = subject.login_as_owner
116
-
117
- owner = owner_access_token.get("/api/v1/users/current.json")['user']
118
-
119
- lambda {
120
- result = owner_access_token.delete("/api/v1/users/#{owner['id']}.json")
121
- }.should raise_error(UserVoice::Unauthorized, /last owner/i)
122
- end
123
-
124
- it "should not be able to delete user without login" do
125
- regular_user = subject.login_as('somebodythere@example.com').get("/api/v1/users/current.json")['user']
126
-
127
- lambda {
128
- subject.delete("/api/v1/users/#{regular_user['id']}.json")
129
- }.should raise_error(UserVoice::Unauthorized)
130
- end
131
-
132
- it "should be able to identify suggestions" do
133
- owner_token = subject.login_as_owner
134
- external_scope='sync_to_moon'
135
- suggestions = owner_token.get("/api/v1/suggestions.json?filter=with_external_id&external_scope=#{external_scope}&manual_action=#{external_scope}")['suggestions']
136
-
137
- identifications = suggestions.map {|s| { :id => s['id'], :external_id => s['id'].to_i*10 } }
138
-
139
- ids = owner_token.put("/api/v1/suggestions/identify.json",
140
- :external_scope => external_scope,
141
- :identifications => identifications)['identifications']['ids']
142
- ids.should == identifications.map { |s| s[:id] }.sort
143
- end
144
-
145
- it "should be able to delete itself" do
146
- my_token = subject.login_as('somebodythere@example.com')
147
-
148
- # whoami
149
- my_id = my_token.get("/api/v1/users/current.json")['user']['id']
150
-
151
- # Delete myself!
152
- my_token.delete("/api/v1/users/#{my_id}.json")['user']['id'].should == my_id
153
-
154
- # I don't exist anymore
155
- lambda {
156
- my_token.get("/api/v1/users/current.json")
157
- }.should raise_error(UserVoice::NotFound)
158
- end
159
-
160
- it "should/be able to delete random user and login as him after that" do
161
- somebody = subject.login_as('somebodythere@example.com')
162
- owner = subject.login_as_owner
163
-
164
- # somebody is still there...
165
- regular_user = somebody.get("/api/v1/users/current.json")['user']
166
- regular_user['email'].should == 'somebodythere@example.com'
167
-
168
- # delete somebody!
169
- owner.delete("/api/v1/users/#{regular_user['id']}.json")['user']['id'].should == regular_user['id']
170
-
171
- # not found anymore!
172
- lambda {
173
- somebody.get("/api/v1/users/current.json")['errors']['type']
174
- }.should raise_error(UserVoice::NotFound)
175
-
176
- # this recreates somebody
177
- somebody = subject.login_as('somebodythere@example.com')
178
- somebody.get("/api/v1/users/current.json")['user']['id'].should_not == regular_user['id']
179
- end
180
-
181
- it "should raise error with invalid email parameter" do
182
- expect { subject.login_as('ma') }.to raise_error(UserVoice::Unauthorized)
183
- expect { subject.login_as(nil) }.to raise_error(UserVoice::Unauthorized)
184
- end
185
- end
186
15
  end
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ describe UserVoice::Client do
3
+ subject { UserVoice::Client.new(config['subdomain_name'],
4
+ config['api_key'],
5
+ config['api_secret'],
6
+ :uservoice_domain => config['uservoice_domain'],
7
+ :protocol => config['protocol']) }
8
+ let(:external_scope) { 'external_system_name' }
9
+
10
+ it "should get user names from the API" do
11
+ users = subject.get("/api/v1/users.json?per_page=3")
12
+ user_names = users['users'].map { |user| user['name'] }
13
+ user_names.all?.should == true
14
+ user_names.size.should == 3
15
+ end
16
+
17
+ it "should not get current user without logged in user" do
18
+ lambda do
19
+ user = subject.get("/api/v1/users/current.json")
20
+ end.should raise_error(UserVoice::Unauthorized)
21
+ end
22
+
23
+ it "should be able to get access token as owner" do
24
+ subject.login_as_owner do |owner|
25
+ owner.get("/api/v1/users/current.json")['user']['roles']['owner'].should == true
26
+
27
+ owner.login_as('regular@example.com') do |regular|
28
+ owner.get("/api/v1/users/current.json")['user']['roles']['owner'].should == true
29
+ @user = regular.get("/api/v1/users/current.json")['user']
30
+ @user['roles']['owner'].should == false
31
+ end
32
+
33
+ owner.get("/api/v1/users/current.json")['user']['roles']['owner'].should == true
34
+ end
35
+ # ensure blocks got run
36
+ @user['email'].should == 'regular@example.com'
37
+ end
38
+
39
+ it "should not be able to create KB article as nobody" do
40
+ lambda do
41
+ result = subject.post("/api/v1/articles.json", :article => {
42
+ :title => 'good morning'
43
+ })
44
+ end.should raise_error(UserVoice::Unauthorized)
45
+ end
46
+
47
+ it "should be able to create and delete a forum as the owner" do
48
+ owner = subject.login_as_owner
49
+ forum = owner.post("/api/v1/forums.json", :forum => {
50
+ :name => 'Test forum from RSpec',
51
+ 'private' => true,
52
+ 'allow_by_email_domain' => true,
53
+ 'allowed_email_domains' => [{'domain' => 'raimo.rspec.example.com'}]
54
+ })['forum']
55
+
56
+ forum['id'].should be_a(Integer)
57
+
58
+ deleted_forum = owner.delete("/api/v1/forums/#{forum['id']}.json")['forum']
59
+ deleted_forum['id'].should == forum['id']
60
+ end
61
+
62
+ it "should get current user with 2-legged call" do
63
+ user = subject.login_as('mailaddress@example.com') do |token|
64
+ token.get("/api/v1/users/current.json")['user']
65
+ end
66
+
67
+ user['email'].should == 'mailaddress@example.com'
68
+ end
69
+
70
+ it "should get current user with copied access token" do
71
+ original_token = subject.login_as('mailaddress@example.com')
72
+
73
+ client = UserVoice::Client.new(config['subdomain_name'],
74
+ config['api_key'],
75
+ config['api_secret'],
76
+ :uservoice_domain => config['uservoice_domain'],
77
+ :protocol => config['protocol'],
78
+ :oauth_token => original_token.token,
79
+ :oauth_token_secret => original_token.secret)
80
+ # Also this works but creates an extra object:
81
+ # client = client.login_with_access_token(original_token.token, original_token.secret)
82
+
83
+ user = client.get("/api/v1/users/current.json")['user']
84
+
85
+ user['email'].should == 'mailaddress@example.com'
86
+ end
87
+
88
+ it "should login as an owner" do
89
+ me = subject.login_as_owner
90
+
91
+ owner = me.get("/api/v1/users/current.json")['user']
92
+ owner['roles']['owner'].should == true
93
+ end
94
+
95
+ it "should not be able to delete when not deleting on behalf of anyone" do
96
+ lambda {
97
+ result = subject.delete("/api/v1/users/#{234}.json")
98
+ }.should raise_error(UserVoice::Unauthorized, /user required/i)
99
+ end
100
+
101
+ it "should not be able to delete owner" do
102
+ owner_access_token = subject.login_as_owner
103
+
104
+ owner = owner_access_token.get("/api/v1/users/current.json")['user']
105
+
106
+ lambda {
107
+ result = owner_access_token.delete("/api/v1/users/#{owner['id']}.json")
108
+ }.should raise_error(UserVoice::Unauthorized, /last owner/i)
109
+ end
110
+
111
+ it "should not be able to delete user without login" do
112
+ regular_user = subject.login_as('somebodythere@example.com').get("/api/v1/users/current.json")['user']
113
+
114
+ lambda {
115
+ subject.delete("/api/v1/users/#{regular_user['id']}.json")
116
+ }.should raise_error(UserVoice::Unauthorized)
117
+ end
118
+
119
+ it 'should get all suggestions using a collection enumerator' do
120
+ subject.should_receive(:get).once.and_return({
121
+ "response_data"=>{"page"=>1, "per_page"=>10, "total_records"=>1, "filter"=>"all", "sort"=>"votes"},
122
+ "suggestions"=>[ {
123
+ "url"=>"http://uservoice-subdomain.uservoice.com/forums/1-a/suggestions/1-i",
124
+ "id"=>1,
125
+ "state"=>"published",
126
+ "title"=>"a",
127
+ "text"=>"b",
128
+ "formatted_text"=>"b",
129
+ "forum"=>{"id"=>"1", "name"=>"General"}
130
+ }
131
+ ]})
132
+ suggestions = subject.get_collection("/api/v1/suggestions.json")
133
+ count = 0
134
+ suggestions.each do |suggestion|
135
+ count += 1
136
+ end
137
+ count.should == suggestions.size
138
+ count.should == 1
139
+ end
140
+
141
+ it "should get an error when trying to query suggestions with an unexistant manual action" do
142
+ lambda {
143
+ subject.login_as_owner do |owner_token|
144
+ owner_token.get("/api/v1/suggestions.json?filter=with_external_id&external_scope=#{external_scope}&manual_action=#{external_scope}")['suggestions']
145
+ end
146
+ }.should raise_error(UserVoice::NotFound)
147
+ end
148
+
149
+ it "should identify a suggestion" do
150
+ owner_token = subject.login_as_owner
151
+
152
+ suggestions = owner_token.get("/api/v1/suggestions.json?filter=without_external_id&external_scope=#{external_scope}&per_page=1")['suggestions']
153
+ identifications = suggestions.map {|s| { :id => s['id'], :external_id => s['id'].to_i*10, :url => 'http://url.example.com' } }
154
+
155
+ ids = owner_token.put("/api/v1/suggestions/identify.json",
156
+ :upsert => true,
157
+ :external_scope => external_scope,
158
+ :identifications => identifications)['identifications']['ids']
159
+ ids.should == identifications.map { |s| s[:id] }.sort
160
+ end
161
+
162
+ it "should be able to delete itself" do
163
+ my_token = subject.login_as('somebodythere@example.com')
164
+
165
+ # whoami
166
+ my_id = my_token.get("/api/v1/users/current.json")['user']['id']
167
+
168
+ # Delete myself!
169
+ my_token.delete("/api/v1/users/#{my_id}.json")['user']['id'].should == my_id
170
+
171
+ # I don't exist anymore
172
+ lambda {
173
+ my_token.get("/api/v1/users/current.json")
174
+ }.should raise_error(UserVoice::NotFound)
175
+ end
176
+
177
+ it "should/be able to delete random user and login as him after that" do
178
+ somebody = subject.login_as('somebodythere@example.com')
179
+ owner = subject.login_as_owner
180
+
181
+ # somebody is still there...
182
+ regular_user = somebody.get("/api/v1/users/current.json")['user']
183
+ regular_user['email'].should == 'somebodythere@example.com'
184
+
185
+ # delete somebody!
186
+ owner.delete("/api/v1/users/#{regular_user['id']}.json")['user']['id'].should == regular_user['id']
187
+
188
+ # not found anymore!
189
+ lambda {
190
+ somebody.get("/api/v1/users/current.json")['errors']['type']
191
+ }.should raise_error(UserVoice::NotFound)
192
+
193
+ # this recreates somebody
194
+ somebody = subject.login_as('somebodythere@example.com')
195
+ somebody.get("/api/v1/users/current.json")['user']['id'].should_not == regular_user['id']
196
+ end
197
+
198
+ it "should raise error with invalid email parameter" do
199
+ expect { subject.login_as('ma') }.to raise_error(UserVoice::Unauthorized)
200
+ expect { subject.login_as(nil) }.to raise_error(UserVoice::Unauthorized)
201
+ end
202
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe UserVoice::Collection do
4
+ PER_PAGE = 500
5
+ ELEMENTS = 1501 # 4 pages, one record in the last page
6
+
7
+ context 'having an empty result set' do
8
+ let(:client) do
9
+ client = mock(
10
+ :get => {"response_data"=>{"page"=>1, "per_page"=>10, "total_records"=>0, "filter"=>"all", "sort"=>"votes"}, "suggestions"=>[]}
11
+ )
12
+ end
13
+ before do
14
+ @collection = UserVoice::Collection.new(client, '/api/v1/suggestions')
15
+ end
16
+
17
+ it "should return size of zero" do
18
+ @collection.size.should == 0
19
+ end
20
+
21
+ it 'should have zero entries with #each' do
22
+ @collection.each do |suggestion|
23
+ raise RuntimeError.new('should be empty')
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'having a list with one element' do
29
+ before do
30
+ @client = mock(
31
+ :get => {"response_data"=>{"page"=>1, "per_page"=>10, "total_records"=>1, "filter"=>"all", "sort"=>"votes"}, "suggestions"=>[ {
32
+ "url"=>"http://uservoice-subdomain.uservoice.com/forums/1-general/suggestions/1-idea",
33
+ "id"=>1,
34
+ "state"=>"published",
35
+ "title"=>"a",
36
+ "text"=>"b",
37
+ "formatted_text"=>"b",
38
+ "forum"=>{"id"=>"1", "name"=>"General"}
39
+ }
40
+ ]})
41
+ @collection = UserVoice::Collection.new(@client, '/api/v1/suggestions')
42
+ end
43
+
44
+ it 'should have correct size' do
45
+ @collection.size.should == 1
46
+ end
47
+
48
+ it 'should yield correct records ids' do
49
+ ids = []
50
+ @collection.each do |val|
51
+ ids.push(val['id'])
52
+ end
53
+ ids.should == [1]
54
+ end
55
+
56
+ it 'should map ids' do
57
+ @collection.map do |val|
58
+ val['id']
59
+ end.should == [1]
60
+ end
61
+
62
+ it 'should collect ids' do
63
+ @client.should_receive(:get).with("/api/v1/suggestions?per_page=#{PER_PAGE}&page=1").once
64
+
65
+ @collection.collect do |val|
66
+ val['id']
67
+ end.should == [1]
68
+
69
+ @collection.map do |val|
70
+ val['id']
71
+ end.should == [1]
72
+ end
73
+ end
74
+
75
+ context 'having a list with 1501 elements' do
76
+
77
+ before do
78
+ @client = mock()
79
+
80
+ 4.times.map do |page_index|
81
+ @client.stub(:get).with("/api/v1/suggestions?per_page=#{PER_PAGE}&page=#{page_index+1}") do
82
+ {
83
+ "response_data" => {"page"=> page_index+1, "per_page" => PER_PAGE, "total_records" => ELEMENTS, "filter"=>"all", "sort"=>"votes"},
84
+ "suggestions"=> (PER_PAGE * page_index + 1).upto([PER_PAGE * (page_index + 1), ELEMENTS].min).map do |idea_index|
85
+ {
86
+ "url"=>"http://uservoice-subdomain.uservoice.com/forums/1-general/suggestions/#{idea_index}-idea",
87
+ "id"=> idea_index,
88
+ "state"=>"published",
89
+ "title"=>"Idea ##{idea_index}",
90
+ "text"=>"Idea ##{idea_index}",
91
+ "formatted_text"=>"Idea ##{idea_index}",
92
+ "forum"=>{"id"=>"1", "name"=>"General"}
93
+ }
94
+ end
95
+ }
96
+ end
97
+ end
98
+ @collection = UserVoice::Collection.new(@client, '/api/v1/suggestions')
99
+ end
100
+
101
+ it 'should have correct size' do
102
+ @client.should_receive(:get).with("/api/v1/suggestions?per_page=#{PER_PAGE}&page=1").once
103
+ @collection.size.should == ELEMENTS
104
+ end
105
+
106
+ it 'should get last element and array size with two api calls' do
107
+ @collection.last['id'].should == ELEMENTS
108
+ @collection.first['id'].should == 1
109
+ end
110
+
111
+ it 'should yield correct records ids' do
112
+ ids = []
113
+ @collection.each do |val|
114
+ ids.push(val['id'])
115
+ end
116
+ ids.size.should == ELEMENTS
117
+ ids.should == 1.upto(ELEMENTS).to_a
118
+ end
119
+
120
+ it 'should map ids' do
121
+ @collection.map do |val|
122
+ val['id']
123
+ end.should == 1.upto(ELEMENTS).to_a
124
+ end
125
+ end
126
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uservoice-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-25 00:00:00.000000000 Z
12
+ date: 2012-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70266109840080 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 1.0.5
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70266109840080
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.5
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: ezcrypto
27
- requirement: &70266109839480 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 0.7.2
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70266109839480
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.7.2
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: json
38
- requirement: &70266109838900 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: 1.7.5
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70266109838900
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.7.5
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: oauth
49
- requirement: &70266109838340 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,7 +69,12 @@ dependencies:
54
69
  version: 0.4.7
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *70266109838340
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.4.7
58
78
  description: The gem provides Ruby-bindings to UserVoice API and helps generating
59
79
  Single-Sign-On tokens.
60
80
  email:
@@ -68,10 +88,14 @@ files:
68
88
  - README.md
69
89
  - Rakefile
70
90
  - lib/uservoice-ruby.rb
91
+ - lib/uservoice/client.rb
92
+ - lib/uservoice/collection.rb
71
93
  - lib/uservoice/user_voice.rb
72
94
  - lib/uservoice/version.rb
73
95
  - spec/config.yml.templ
74
96
  - spec/lib/user_voice_spec.rb
97
+ - spec/lib/uservoice/client_spec.rb
98
+ - spec/lib/uservoice/collection_spec.rb
75
99
  - spec/spec_helper.rb
76
100
  - uservoice-ruby.gemspec
77
101
  homepage: http://developer.uservoice.com
@@ -94,11 +118,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
118
  version: '0'
95
119
  requirements: []
96
120
  rubyforge_project: uservoice-ruby
97
- rubygems_version: 1.8.15
121
+ rubygems_version: 1.8.23
98
122
  signing_key:
99
123
  specification_version: 3
100
124
  summary: Client library for UserVoice API
101
125
  test_files:
102
126
  - spec/config.yml.templ
103
127
  - spec/lib/user_voice_spec.rb
128
+ - spec/lib/uservoice/client_spec.rb
129
+ - spec/lib/uservoice/collection_spec.rb
104
130
  - spec/spec_helper.rb