omnicontacts 0.3.4 → 0.3.5

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.
@@ -9,21 +9,29 @@ module OmniContacts
9
9
 
10
10
  attr_reader :auth_host, :authorize_path, :auth_token_path, :scope
11
11
 
12
- def initialize *args
13
- super *args
14
- @auth_host = "oauth.live.com"
15
- @authorize_path = "/authorize"
16
- @scope = "wl.signin, wl.basic, wl.birthday , wl.emails ,wl.contacts_birthday , wl.contacts_photos"
17
- @auth_token_path = "/token"
12
+ def initialize app, client_id, client_secret, options ={}
13
+ super app, client_id, client_secret, options
14
+ @auth_host = "login.live.com"
15
+ @authorize_path = "/oauth20_authorize.srf"
16
+ @scope = options[:permissions] || "wl.signin, wl.basic, wl.birthday , wl.emails ,wl.contacts_birthday , wl.contacts_photos"
17
+ @auth_token_path = "/oauth20_token.srf"
18
18
  @contacts_host = "apis.live.net"
19
19
  @contacts_path = "/v5.0/me/contacts"
20
+ @self_path = "/v5.0/me"
20
21
  end
21
22
 
22
23
  def fetch_contacts_using_access_token access_token, access_token_secret
24
+ fetch_current_user(access_token)
23
25
  contacts_response = https_get(@contacts_host, @contacts_path, :access_token => access_token)
24
26
  contacts_from_response contacts_response
25
27
  end
26
28
 
29
+ def fetch_current_user access_token
30
+ self_response = https_get(@contacts_host, @self_path, :access_token => access_token)
31
+ user = current_user self_response
32
+ set_current_user user
33
+ end
34
+
27
35
  private
28
36
 
29
37
  def contacts_from_response response_as_json
@@ -31,10 +39,11 @@ module OmniContacts
31
39
  contacts = []
32
40
  response['data'].each do |entry|
33
41
  # creating nil fields to keep the fields consistent across other networks
34
- contact = {:id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil, :gender => nil, :birthday => nil, :profile_picture=> nil, :relation => nil}
35
- contact[:id] = entry['user_id']
42
+ contact = {:id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil, :gender => nil, :birthday => nil, :profile_picture=> nil, :relation => nil, :email_hashes => []}
43
+ contact[:id] = entry['user_id'] ? entry['user_id'] : entry['id']
36
44
  if valid_email? entry["name"]
37
45
  contact[:email] = entry["name"]
46
+ contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:email])
38
47
  else
39
48
  contact[:first_name] = normalize_name(entry['first_name'])
40
49
  contact[:last_name] = normalize_name(entry['last_name'])
@@ -42,12 +51,34 @@ module OmniContacts
42
51
  end
43
52
  contact[:birthday] = birthday_format(entry['birth_month'], entry['birth_day'], entry['birth_year'])
44
53
  contact[:gender] = entry['gender']
45
- contact[:profile_picture] = 'https://apis.live.net/v5.0/' + entry['user_id'] + '/picture' if entry['user_id']
54
+ contact[:profile_picture] = image_url(entry['user_id'])
55
+ contact[:email_hashes] = entry['email_hashes']
46
56
  contacts << contact if contact[:name] || contact[:first_name]
47
57
  end
48
58
  contacts
49
59
  end
50
60
 
61
+ def parse_email(emails)
62
+ return nil if emails.nil?
63
+ emails['account']
64
+ end
65
+
66
+ def current_user me
67
+ return nil if me.nil?
68
+ me = JSON.parse(me)
69
+ email = parse_email(me['emails'])
70
+ user = {:id => me['id'], :email => email, :name => me['name'], :first_name => me['first_name'],
71
+ :last_name => me['last_name'], :gender => me['gender'], :profile_picture => image_url(me['id']),
72
+ :birthday => birthday_format(me['birth_month'], me['birth_day'], me['birth_year'])
73
+ }
74
+ user
75
+ end
76
+
77
+
78
+ def image_url hotmail_id
79
+ return 'https://apis.live.net/v5.0/' + hotmail_id + '/picture' if hotmail_id
80
+ end
81
+
51
82
  def escape_windows_format value
52
83
  value.gsub(/[\r\s]/, '')
53
84
  end
@@ -20,11 +20,19 @@ module OmniContacts
20
20
 
21
21
  def fetch_contacts_from_token_and_verifier auth_token, auth_token_secret, auth_verifier
22
22
  (access_token, access_token_secret, guid) = fetch_access_token(auth_token, auth_token_secret, auth_verifier, ['xoauth_yahoo_guid'])
23
+ fetch_current_user(access_token, access_token_secret, guid)
23
24
  contacts_path = "/v1/user/#{guid}/contacts"
24
- contacts_response = http_get(@contacts_host, contacts_path, contacts_req_params(access_token, access_token_secret, contacts_path))
25
+ contacts_response = https_get(@contacts_host, contacts_path, contacts_req_params(access_token, access_token_secret, contacts_path))
25
26
  contacts_from_response contacts_response
26
27
  end
27
28
 
29
+ def fetch_current_user access_token, access_token_secret, guid
30
+ self_path = "/v1/user/#{guid}/profile"
31
+ self_response = https_get(@contacts_host, self_path, contacts_req_params(access_token, access_token_secret, self_path))
32
+ user = current_user self_response
33
+ set_current_user user
34
+ end
35
+
28
36
  private
29
37
 
30
38
  def contacts_req_params access_token, access_token_secret, contacts_path
@@ -35,10 +43,9 @@ module OmniContacts
35
43
  :oauth_signature_method => 'HMAC-SHA1',
36
44
  :oauth_timestamp => timestamp,
37
45
  :oauth_token => access_token,
38
- :oauth_version => OmniContacts::Authorization::OAuth1::OAUTH_VERSION,
39
- :view => 'compact'
46
+ :oauth_version => OmniContacts::Authorization::OAuth1::OAUTH_VERSION
40
47
  }
41
- contacts_url = "http://#{@contacts_host}#{contacts_path}"
48
+ contacts_url = "https://#{@contacts_host}#{contacts_path}"
42
49
  params['oauth_signature'] = oauth_signature('GET', contacts_url, params, access_token_secret)
43
50
  params
44
51
  end
@@ -49,34 +56,120 @@ module OmniContacts
49
56
  return contacts unless response['contacts']['contact']
50
57
  response['contacts']['contact'].each do |entry|
51
58
  # creating nil fields to keep the fields consistent across other networks
52
- contact = {:id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil, :gender => nil, :birthday => nil, :profile_picture=> nil, :relation => nil}
59
+ contact = { :id => nil,
60
+ :first_name => nil,
61
+ :last_name => nil,
62
+ :name => nil,
63
+ :email => nil,
64
+ :gender => nil,
65
+ :birthday => nil,
66
+ :profile_picture=> nil,
67
+ :address_1 => nil,
68
+ :address_2 => nil,
69
+ :city => nil,
70
+ :region => nil,
71
+ :postcode => nil,
72
+ :relation => nil }
53
73
  yahoo_id = nil
54
74
  contact[:id] = entry['id'].to_s
55
75
  entry['fields'].each do |field|
56
- if field['type'] == 'name'
76
+ case field['type']
77
+ when 'name'
57
78
  contact[:first_name] = normalize_name(field['value']['givenName'])
58
79
  contact[:last_name] = normalize_name(field['value']['familyName'])
59
80
  contact[:name] = full_name(contact[:first_name],contact[:last_name])
60
- end
61
- contact[:email] = field['value'] if field['type'] == 'email'
62
-
63
- if field['type'] == 'yahooid'
81
+ when 'email'
82
+ contact[:email] = field['value'] if field['type'] == 'email'
83
+ when 'yahooid'
64
84
  yahoo_id = field['value']
85
+ when 'address'
86
+ value = field['value']
87
+ contact[:address_1] = street = value['street']
88
+ if street.index("\n")
89
+ parts = street.split("\n")
90
+ contact[:address_1] = parts.first
91
+ contact[:address_2] = parts[1..-1].join(', ')
92
+ end
93
+ contact[:city] = value['city']
94
+ contact[:region] = value['stateOrProvince']
95
+ contact[:postcode] = value['postalCode']
96
+ when 'birthday'
97
+ contact[:birthday] = birthday_format(field['value']['month'], field['value']['day'],field['value']['year'])
65
98
  end
66
-
67
99
  contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:email]) if contact[:name].nil? && contact[:email]
68
100
  # contact[:first_name], contact[:last_name], contact[:name] = email_to_name(yahoo_id) if (yahoo_id && contact[:name].nil? && contact[:email].nil?)
69
101
 
70
- if field['type'] == 'birthday'
71
- contact[:birthday] = birthday_format(field['value']['month'], field['value']['day'],field['value']['year'])
102
+ if yahoo_id
103
+ contact[:profile_picture] = image_url(yahoo_id)
104
+ else
105
+ contact[:profile_picture] = image_url_from_email(contact[:email])
72
106
  end
73
107
  end
74
108
  contacts << contact if contact[:name]
75
109
  end
76
- contacts.uniq! {|c| c[:email] || c[:name]}
110
+ contacts.uniq! {|c| c[:email] || c[:profile_picture] || c[:name]}
77
111
  contacts
78
112
  end
79
113
 
114
+ def image_url yahoo_id
115
+ return 'https://img.msg.yahoo.com/avatar.php?yids=' + yahoo_id if yahoo_id
116
+ end
117
+
118
+ def parse_email(emails)
119
+ return nil if emails.nil?
120
+ email = nil
121
+ if emails.is_a?(Hash)
122
+ if emails.has_key?("primary")
123
+ email = emails['handle']
124
+ end
125
+ elsif emails.is_a?(Array)
126
+ emails.each do |e|
127
+ if e.has_key?('primary') && e['primary']
128
+ email = e['handle']
129
+ break
130
+ end
131
+ end
132
+ end
133
+ email
134
+ end
135
+
136
+ def birthday dob
137
+ return nil if dob.nil?
138
+ birthday = dob.split('/')
139
+ return birthday_format(birthday[0], birthday[1], birthday[3]) if birthday.size == 3
140
+ return birthday_format(birthday[0], birthday[1], nil) if birthday.size == 2
141
+
142
+ end
143
+
144
+ def gender g
145
+ return "female" if g == "F"
146
+ return "male" if g == "M"
147
+ end
148
+
149
+ def my_image img
150
+ return nil if img.nil?
151
+ return img['imageUrl']
152
+ end
153
+
154
+ def current_user me
155
+ return nil if me.nil?
156
+ me = JSON.parse(me)
157
+ me = me['profile']
158
+ email = parse_email(me['emails'])
159
+ user = {:id => me["guid"], :email => email, :name => full_name(me['givenName'],me['familyName']), :first_name => normalize_name(me['givenName']),
160
+ :last_name => normalize_name(me['familyName']), :gender => gender(me['gender']), :birthday => birthday(me['birthdate']),
161
+ :profile_picture => my_image(me['image'])
162
+ }
163
+ user
164
+ end
165
+
166
+ #def profile_image_url(guid, access_token, access_token_secret)
167
+ # image_path = "/v1/user/#{guid}/profile/image/48x48"
168
+ # response = https_get(@contacts_host, image_path, contacts_req_params(access_token, access_token_secret, image_path))
169
+ # image_data = JSON.parse(response)
170
+ # return image_data['image']['imageUrl'] if image_data['image']
171
+ # return nil
172
+ #end
80
173
  end
81
174
  end
82
175
  end
@@ -13,7 +13,7 @@ module OmniContacts
13
13
 
14
14
  def initialize app, options
15
15
  @app = app
16
- @listening_path = "/contacts/" + class_name
16
+ @listening_path = MOUNT_PATH + class_name
17
17
  @ssl_ca_file = options[:ssl_ca_file]
18
18
  end
19
19
 
@@ -69,6 +69,10 @@ module OmniContacts
69
69
  end
70
70
  end
71
71
 
72
+ def set_current_user user
73
+ @env["omnicontacts.user"] = user
74
+ end
75
+
72
76
  # This method rescues executes a block of code and
73
77
  # rescue all exceptions. In case of an exception the
74
78
  # user is redirected to the failure endpoint.
@@ -83,8 +87,10 @@ module OmniContacts
83
87
  end
84
88
 
85
89
  def handle_error error_type, exception
86
- logger << ("Error #{error_type} while processing #{@env["PATH_INFO"]}: #{exception.message}") if logger
87
- [302, {"Content-Type" => "text/html", "location" => "/contacts/failure?error_message=#{error_type}&importer=#{class_name}"}, []]
90
+ logger.puts("Error #{error_type} while processing #{@env["PATH_INFO"]}: #{exception.message}") if logger
91
+ failure_url = "#{ MOUNT_PATH }failure?error_message=#{error_type}&importer=#{class_name}"
92
+ target_url = append_state_query(failure_url)
93
+ [302, {"Content-Type" => "text/html", "location" => target_url}, []]
88
94
  end
89
95
 
90
96
  def session
@@ -100,6 +106,15 @@ module OmniContacts
100
106
  "omnicontacts." + class_name
101
107
  end
102
108
 
109
+ def append_state_query(target_url)
110
+ state = Rack::Utils.parse_query(@env['QUERY_STRING'])['state']
111
+
112
+ unless state.nil?
113
+ target_url = target_url + (target_url.include?("?")?"&":"?") + 'state=' + state
114
+ end
115
+
116
+ return target_url
117
+ end
103
118
  end
104
119
  end
105
120
  end
@@ -18,7 +18,7 @@ module OmniContacts
18
18
  super app, options
19
19
  @consumer_key = consumer_key
20
20
  @consumer_secret = consumer_secret
21
- @callback_path = options[:callback_path] || "/contacts/#{class_name}/callback"
21
+ @callback_path = options[:callback_path] || "#{ MOUNT_PATH }#{class_name}/callback"
22
22
  @token_prop_name = "#{base_prop_name}.oauth_token"
23
23
  end
24
24
 
@@ -43,7 +43,9 @@ module OmniContacts
43
43
  end
44
44
 
45
45
  def redirect_to_authorization_site auth_token
46
- [302, {"Content-Type" => "application/x-www-form-urlencoded", "location" => authorization_url(auth_token)}, []]
46
+ authorization_url = authorization_url(auth_token)
47
+ target_url = append_state_query(authorization_url)
48
+ [302, {"Content-Type" => "application/x-www-form-urlencoded", "location" => target_url}, []]
47
49
  end
48
50
 
49
51
  # Parses the authorization token from the query string and
@@ -19,12 +19,13 @@ module OmniContacts
19
19
  super app, options
20
20
  @client_id = client_id
21
21
  @client_secret = client_secret
22
- @redirect_path = options[:redirect_path] || "/contacts/#{class_name}/callback"
22
+ @redirect_path = options[:redirect_path] || "#{ MOUNT_PATH }#{class_name}/callback"
23
23
  @ssl_ca_file = options[:ssl_ca_file]
24
24
  end
25
25
 
26
26
  def request_authorization_from_user
27
- [302, {"Content-Type" => "application/x-www-form-urlencoded", "location" => authorization_url}, []]
27
+ target_url = append_state_query(authorization_url)
28
+ [302, {"location" => target_url}, []]
28
29
  end
29
30
 
30
31
  def redirect_uri
@@ -12,7 +12,6 @@ module OmniContacts
12
12
  def normalize_name name
13
13
  return nil if name.nil?
14
14
  name.chomp!
15
- name = name.split(' ').map(&:capitalize).join(' ')
16
15
  name.squeeze!(' ')
17
16
  name.strip!
18
17
  return name
@@ -38,5 +37,20 @@ module OmniContacts
38
37
  return username, nil, username
39
38
  end
40
39
 
40
+ # create an image_url from a gmail or yahoo email id.
41
+ def image_url_from_email email
42
+ return nil if email.nil? || !email.include?('@')
43
+ username, domain = *(email.split('@'))
44
+ return nil if username.nil? or domain.nil?
45
+ gmail_base_url = "https://profiles.google.com/s2/photos/profile/"
46
+ yahoo_base_url = "https://img.msg.yahoo.com/avatar.php?yids="
47
+ if domain.include?('gmail')
48
+ image_url = gmail_base_url + username
49
+ elsif domain.include?('yahoo')
50
+ image_url = yahoo_base_url + username
51
+ end
52
+ image_url
53
+ end
54
+
41
55
  end
42
56
  end
@@ -4,8 +4,8 @@ require File.expand_path('../lib/omnicontacts', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = 'omnicontacts'
6
6
  gem.description = %q{A generalized Rack middleware for importing contacts from major email providers.}
7
- gem.authors = ['Diego Castorina']
8
- gem.email = ['diegocastorina@gmail.com']
7
+ gem.authors = ['Diego Castorina', 'Jordan Lance']
8
+ gem.email = ['diegocastorina@gmail.com', 'voorruby@gmail.com']
9
9
 
10
10
  gem.add_runtime_dependency 'rack'
11
11
  gem.add_runtime_dependency 'json'
@@ -76,7 +76,7 @@ describe OmniContacts::Authorization::OAuth1 do
76
76
  end
77
77
 
78
78
  describe "oauth_signature" do
79
- subject { test_target.oauth_signature("GET", "http://social.yahooapis.com/v1/user", {:name => "diego", :surname => "castorina"}, "secret2") }
80
- it { should eq("ZqWoQISWcuz%2FSDnDxWihtsFDKwc%3D") }
79
+ subject { test_target.oauth_signature("GET", "https://social.yahooapis.com/v1/user", {:name => "diego", :surname => "castorina"}, "secret2") }
80
+ it { should eq("xfumZoyVYUbHXSAafdha3HZUqQg%3D") }
81
81
  end
82
82
  end
@@ -12,6 +12,12 @@ describe OmniContacts::HTTPUtils do
12
12
  result.should eq("surname=doe&name=john")
13
13
  end
14
14
  end
15
+
16
+ it "should work for integer values in the map" do
17
+ result = OmniContacts::HTTPUtils.to_query_string(:client_id => 1234, :secret => "1234HJL8")
18
+ result.should eq("client_id=1234&secret=1234HJL8")
19
+ end
20
+
15
21
  end
16
22
 
17
23
  describe "encode" do
@@ -5,6 +5,33 @@ describe OmniContacts::Importer::Facebook do
5
5
 
6
6
  let(:facebook) { OmniContacts::Importer::Facebook.new({}, "client_id", "client_secret") }
7
7
 
8
+ let(:self_response) {
9
+ '{
10
+ "first_name":"Chris",
11
+ "last_name":"Johnson",
12
+ "name":"Chris Johnson",
13
+ "id":"543216789",
14
+ "gender":"male",
15
+ "birthday":"06/21/1982",
16
+ "significant_other":{"id": "243435322"},
17
+ "relationship_status": "Married",
18
+ "picture":{"data":{"url":"http://profile.ak.fbcdn.net/hprofile-ak-snc6/186364_543216789_2089044200_q.jpg","is_silhouette":false}},
19
+ "email": "chrisjohnson@gmail.com"
20
+ }'
21
+ }
22
+
23
+ let(:spouse_response) {
24
+ '{
25
+ "first_name":"Mary",
26
+ "last_name":"Johnson",
27
+ "name":"Mary Johnson",
28
+ "id":"243435322",
29
+ "gender":"female",
30
+ "birthday":"01/21",
31
+ "picture":{"data":{"url":"http://profile.ak.fbcdn.net/hprofile-ak-snc6/186364_243435322_2089044200_q.jpg","is_silhouette":false}}
32
+ }'
33
+ }
34
+
8
35
  let(:contacts_as_json) {
9
36
  '{"data":[
10
37
  {
@@ -24,23 +51,39 @@ describe OmniContacts::Importer::Facebook do
24
51
  let(:token) { "token" }
25
52
  let(:token_type) { "token_type" }
26
53
 
54
+ before(:each) do
55
+ facebook.instance_variable_set(:@env, {})
56
+ end
27
57
 
28
- it "should request the contacts by providing the token in the url and fields params only for family and friends requests" do
58
+ it "should request the contacts by providing the token in the url" do
59
+ facebook.should_receive(:https_get) do |host, self_path, params, headers|
60
+ params[:access_token].should eq(token)
61
+ params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture,relationship_status,significant_other,email')
62
+ self_response
63
+ end
64
+ facebook.should_receive(:https_get) do |host, spouse_path, params, headers|
65
+ params[:access_token].should eq(token)
66
+ params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture')
67
+ spouse_response
68
+ end
29
69
  facebook.should_receive(:https_get) do |host, path, params, headers|
30
70
  params[:access_token].should eq(token)
31
- params[:fields].should be_nil
71
+ params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture,relationship')
32
72
  contacts_as_json
33
- end
73
+ end.exactly(1).times
34
74
  facebook.should_receive(:https_get) do |host, path, params, headers|
35
75
  params[:access_token].should eq(token)
36
76
  params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture')
37
77
  contacts_as_json
38
- end.at_most(2).times
78
+ end.exactly(1).times
79
+
39
80
  facebook.fetch_contacts_using_access_token token, token_type
40
81
  end
41
82
 
42
83
  it "should correctly parse id, name,email,gender, birthday, profile picture and relation" do
43
- 3.times { facebook.should_receive(:https_get).and_return(contacts_as_json) }
84
+ 1.times { facebook.should_receive(:https_get).and_return(self_response) }
85
+ 1.times { facebook.should_receive(:https_get) }
86
+ 2.times { facebook.should_receive(:https_get).and_return(contacts_as_json) }
44
87
  result = facebook.fetch_contacts_using_access_token token, token_type
45
88
  result.size.should be(1)
46
89
  result.first[:id].should eq('608061886')
@@ -50,9 +93,28 @@ describe OmniContacts::Importer::Facebook do
50
93
  result.first[:email].should be_nil
51
94
  result.first[:gender].should eq('male')
52
95
  result.first[:birthday].should eq({:day=>21, :month=>06, :year=>nil})
53
- result.first[:profile_picture].should eq('http://profile.ak.fbcdn.net/hprofile-ak-snc6/186364_608061886_2089044200_q.jpg')
96
+ result.first[:profile_picture].should eq('https://graph.facebook.com/608061886/picture')
54
97
  result.first[:relation].should eq('cousin')
55
98
  end
99
+
100
+ it "should correctly parse and set logged in user information" do
101
+ 1.times { facebook.should_receive(:https_get).and_return(self_response) }
102
+ 1.times { facebook.should_receive(:https_get) }
103
+ 2.times { facebook.should_receive(:https_get).and_return(contacts_as_json) }
104
+
105
+ facebook.fetch_contacts_using_access_token token, token_type
106
+
107
+ user = facebook.instance_variable_get(:@env)["omnicontacts.user"]
108
+ user.should_not be_nil
109
+ user[:id].should eq("543216789")
110
+ user[:first_name].should eq("Chris")
111
+ user[:last_name].should eq("Johnson")
112
+ user[:name].should eq("Chris Johnson")
113
+ user[:email].should eq("chrisjohnson@gmail.com")
114
+ user[:gender].should eq("male")
115
+ user[:birthday].should eq({:day=>21, :month=>06, :year=>1982})
116
+ user[:profile_picture].should eq("https://graph.facebook.com/543216789/picture")
117
+ end
56
118
  end
57
119
 
58
120
  end