omnicontacts 0.3.7 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f04a2a074a319b00ef72e6ad8a66aa6f36df36d80dd4287858e7f69afda4ee07
4
+ data.tar.gz: 7f42b858981c8b127d3fedbaff922fc118ff36f7b99d96131419bf7266b0382b
5
+ SHA512:
6
+ metadata.gz: b870ef5a5e649857aadd7f470a64ab521667e78b58d3c2c88d55b7e00b4c4f61876f8883ad5e81cfaac3b582d769f017b27add5d0feef81516636f4a69fb32bb
7
+ data.tar.gz: a61f7a7d58d0d67df5a7ee118720e830435c5705ba953934c26b88086e24c512cb3e35d49fb84d7cf50e143599b5178de89aa387a18b67dc22faf69146fc82f7
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- omnicontacts (0.3.5)
4
+ omnicontacts (0.3.9)
5
5
  json
6
6
  rack
7
7
 
@@ -9,7 +9,7 @@ GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
11
  diff-lcs (1.1.3)
12
- json (1.8.1)
12
+ json (1.8.3)
13
13
  multi_json (1.1.0)
14
14
  rack (1.4.1)
15
15
  rack-test (0.6.1)
@@ -37,3 +37,6 @@ DEPENDENCIES
37
37
  rake
38
38
  rspec
39
39
  simplecov
40
+
41
+ BUNDLED WITH
42
+ 1.10.6
data/README.md CHANGED
@@ -32,9 +32,10 @@ require "omnicontacts"
32
32
 
33
33
  Rails.application.middleware.use OmniContacts::Builder do
34
34
  importer :gmail, "client_id", "client_secret", {:redirect_path => "/oauth2callback", :ssl_ca_file => "/etc/ssl/certs/curl-ca-bundle.crt"}
35
- importer :yahoo, "consumer_id", "consumer_secret", {:callback_path => '/callback'}
35
+ importer :yahoo, "consumer_id", "consumer_secret", {:callback_path => "/callback"}
36
36
  importer :linkedin, "consumer_id", "consumer_secret", {:redirect_path => "/oauth2callback", :state => '<long_unique_string_value>'}
37
37
  importer :hotmail, "client_id", "client_secret"
38
+ importer :outlook, "app_id", "app_secret"
38
39
  importer :facebook, "client_id", "client_secret"
39
40
  end
40
41
 
@@ -49,17 +50,20 @@ On the other hand it makes things much easier to leave the default value for `:r
49
50
 
50
51
  * For Gmail : [Google API Console](https://code.google.com/apis/console/)
51
52
 
52
- * For Yahoo : [Yahoo Developer Network](https://developer.apps.yahoo.com/projects)
53
+ * For Yahoo : [Yahoo Developer Network](https://developer.yahoo.com/social/contacts/)
53
54
 
54
55
  * For Hotmail : [Microsoft Developer Network](https://account.live.com/developers/applications/index)
55
56
 
57
+ * For Outlook : [Microsoft Application Registration Portal](https://apps.dev.microsoft.com/)
58
+
56
59
  * For Facebook : [Facebook Developers](https://developers.facebook.com/apps)
57
60
 
58
- * For Linkedin : [Linkedin Developer Network](https://www.linkedin.com/secure/developer)
61
+ * For Linkedin : [Linkedin Developer Network](https://www.linkedin.com/secure/developer)
59
62
 
60
63
 
61
64
  ##### Note:
62
- Please go through [MSDN](http://msdn.microsoft.com/en-us/library/cc287659.aspx) if above Hotmail link will not work.
65
+ Please go through [MSDN](http://msdn.microsoft.com/en-us/library/cc287659.aspx) if above Hotmail link will not work.
66
+ Outlook is a newer Microsoft API which allows to retrieve real email address instead of `email_hashes` when using Hotmail, it also works with all kinds of MS accounts (Office 365, Hotmail.com, Live.com, MSN.com, Outlook.com, and Passport.com).
63
67
 
64
68
  ## Integrating with your Application
65
69
 
@@ -172,7 +176,26 @@ The following table shows which fields are supported by which provider:
172
176
  <td></td>
173
177
  </tr>
174
178
  <tr>
175
- <td>Linkedin</td>
179
+ <td>Outlook</td>
180
+ <td>X</td>
181
+ <td>X</td>
182
+ <td></td>
183
+ <td>X</td>
184
+ <td>X</td>
185
+ <td>X</td>
186
+ <td>X</td>
187
+ <td></td>
188
+ <td>X</td>
189
+ <td>X</td>
190
+ <td>X</td>
191
+ <td>X</td>
192
+ <td></td>
193
+ <td>X</td>
194
+ <td></td>
195
+ <td></td>
196
+ </tr>
197
+ <tr>
198
+ <td>Linkedin</td>
176
199
  <td></td>
177
200
  <td>X</td>
178
201
  <td>X</td>
@@ -214,7 +237,7 @@ If the user does not authorize your application to access his/her contacts list,
214
237
 
215
238
  OmniContacts supports OAuth 1.0 and OAuth 2.0 token refresh, but for both it needs to persist data between requests. OmniContacts stores access tokens in the session. If you hit the 4KB cookie storage limit you better opt for the Memcache or the Active Record storage.
216
239
 
217
- Gmail requires you to register the redirect_path on their website along with your application. Make sure to use the same value present in the configuration file, or `/contacts/gmail/callback` if using the default.
240
+ Gmail requires you to register the redirect_path on their website along with your application. Make sure to use the same value present in the configuration file, or `/contacts/gmail/callback` if using the default. Also make sure that your full url is used including "www" if your site redirects from the root domain.
218
241
 
219
242
  To configure the max number of contacts to download from Gmail, just add a max results parameter in your initializer:
220
243
 
@@ -246,6 +269,11 @@ The `mock` method allows to configure per-provider the result to return:
246
269
 
247
270
  You can either pass a single hash or an array of hashes. If you pass a string, an error will be triggered with subsequent redirect to `/contacts/failure?error_message=internal_error`
248
271
 
272
+ You can also pass a user to fill `omnicontacts.user` (optional)
273
+ ```ruby
274
+ OmniContacts.integration_test.mock(:provider_name, {:email => "contact@example.com"}, {:email => "user@example.com"})
275
+ ```
276
+
249
277
  Follows a full example of an integration test:
250
278
 
251
279
  ```ruby
@@ -1,6 +1,6 @@
1
1
  module OmniContacts
2
2
 
3
- VERSION = "0.3.7"
3
+ VERSION = "0.3.10"
4
4
 
5
5
  MOUNT_PATH = "/contacts/"
6
6
 
@@ -12,7 +12,8 @@ module OmniContacts
12
12
  end
13
13
 
14
14
  def rack14?
15
- Rack.release.split('.')[1].to_i >= 4
15
+ v = Rack.release.split('.')
16
+ v[0].to_i >= 1 || v[1].to_i >= 4
16
17
  end
17
18
 
18
19
  def importer importer, *args
@@ -4,6 +4,7 @@ module OmniContacts
4
4
  autoload :Gmail, "omnicontacts/importer/gmail"
5
5
  autoload :Yahoo, "omnicontacts/importer/yahoo"
6
6
  autoload :Hotmail, "omnicontacts/importer/hotmail"
7
+ autoload :Outlook, "omnicontacts/importer/outlook"
7
8
  autoload :Facebook, "omnicontacts/importer/facebook"
8
9
  autoload :Linkedin, "omnicontacts/importer/linkedin"
9
10
 
@@ -13,12 +13,12 @@ module OmniContacts
13
13
  super *args
14
14
  @auth_host = 'graph.facebook.com'
15
15
  @authorize_path = '/oauth/authorize'
16
- @scope = 'email,user_relationships,user_birthday'
16
+ @scope = 'email,user_relationships,user_birthday,user_friends'
17
17
  @auth_token_path = '/oauth/access_token'
18
18
  @contacts_host = 'graph.facebook.com'
19
- @friends_path = '/me/friends'
20
- @family_path = '/me/family'
21
- @self_path = '/me'
19
+ @friends_path = '/v2.5/me/friends'
20
+ @family_path = '/v2.5/me/family'
21
+ @self_path = '/v2.5/me'
22
22
  end
23
23
 
24
24
  def fetch_contacts_using_access_token access_token, access_token_secret
@@ -13,18 +13,18 @@ module OmniContacts
13
13
  @auth_host = "accounts.google.com"
14
14
  @authorize_path = "/o/oauth2/auth"
15
15
  @auth_token_path = "/o/oauth2/token"
16
- @scope = (args[3] && args[3][:scope]) || "https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
16
+ @scope = (args[3] && args[3][:scope]) || "https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
17
17
  @contacts_host = "www.google.com"
18
18
  @contacts_path = "/m8/feeds/contacts/default/full"
19
19
  @max_results = (args[3] && args[3][:max_results]) || 100
20
20
  @self_host = "www.googleapis.com"
21
- @profile_path = "/oauth2/v1/userinfo"
21
+ @profile_path = "/oauth2/v3/userinfo"
22
22
  end
23
23
 
24
24
  def fetch_contacts_using_access_token access_token, token_type
25
25
  fetch_current_user(access_token, token_type)
26
26
  contacts_response = https_get(@contacts_host, @contacts_path, contacts_req_params, contacts_req_headers(access_token, token_type))
27
- contacts_from_response contacts_response
27
+ contacts_from_response(contacts_response, access_token)
28
28
  end
29
29
 
30
30
  def fetch_current_user access_token, token_type
@@ -43,7 +43,7 @@ module OmniContacts
43
43
  {"GData-Version" => "3.0", "Authorization" => "#{token_type} #{token}"}
44
44
  end
45
45
 
46
- def contacts_from_response response_as_json
46
+ def contacts_from_response(response_as_json, access_token)
47
47
  response = JSON.parse(response_as_json)
48
48
 
49
49
  return [] if response['feed'].nil? || response['feed']['entry'].nil?
@@ -115,7 +115,7 @@ module OmniContacts
115
115
 
116
116
  new_address[:address_1] = address['gd$street']['$t'] if address['gd$street']
117
117
  new_address[:address_1] = address['gd$formattedAddress']['$t'] if new_address[:address_1].nil? && address['gd$formattedAddress']
118
- if new_address[:address_1].index("\n")
118
+ if !new_address[:address_1].nil? && new_address[:address_1].index("\n")
119
119
  parts = new_address[:address_1].split("\n")
120
120
  new_address[:address_1] = parts.first
121
121
  # this may contain city/state/zip if user jammed it all into one string.... :-(
@@ -151,11 +151,13 @@ module OmniContacts
151
151
  # Support older versions of the gem by keeping singular entries around
152
152
  contact[:phone_number] = contact[:phone_numbers][0][:number] if contact[:phone_numbers][0]
153
153
 
154
- if entry['gContact$website'] && entry['gContact$website'][0]["rel"] == "profile"
155
- contact[:id] = contact_id(entry['gContact$website'][0]["href"])
156
- contact[:profile_picture] = image_url(contact[:id])
157
- else
158
- contact[:profile_picture] = image_url_from_email(contact[:email])
154
+ if entry["link"] && entry["link"].is_a?(Array)
155
+ entry["link"].each do |link|
156
+ if link["type"] == 'image/*' && link["gd$etag"]
157
+ contact[:profile_picture] = link["href"] + "?&access_token=" + access_token
158
+ break
159
+ end
160
+ end
159
161
  end
160
162
 
161
163
  if entry['gContact$event']
@@ -180,15 +182,11 @@ module OmniContacts
180
182
  contacts
181
183
  end
182
184
 
183
- def image_url gmail_id
184
- return "https://profiles.google.com/s2/photos/profile/" + gmail_id if gmail_id
185
- end
186
-
187
185
  def current_user me, access_token, token_type
188
186
  return nil if me.nil?
189
187
  me = JSON.parse(me)
190
188
  user = {:id => me['id'], :email => me['email'], :name => me['name'], :first_name => me['given_name'],
191
- :last_name => me['family_name'], :gender => me['gender'], :birthday => birthday(me['birthday']), :profile_picture => image_url(me['id']),
189
+ :last_name => me['family_name'], :gender => me['gender'], :birthday => birthday(me['birthday']), :profile_picture => me["picture"],
192
190
  :access_token => access_token, :token_type => token_type
193
191
  }
194
192
  user
@@ -13,7 +13,7 @@ module OmniContacts
13
13
  super app, client_id, client_secret, options
14
14
  @auth_host = "login.live.com"
15
15
  @authorize_path = "/oauth20_authorize.srf"
16
- @scope = options[:permissions] || "wl.signin, wl.basic, wl.birthday , wl.emails ,wl.contacts_birthday , wl.contacts_photos"
16
+ @scope = options[:permissions] || "wl.signin, wl.basic, wl.birthday , wl.emails ,wl.contacts_birthday , wl.contacts_photos, wl.contacts_emails"
17
17
  @auth_token_path = "/oauth20_token.srf"
18
18
  @contacts_host = "apis.live.net"
19
19
  @contacts_path = "/v5.0/me/contacts"
@@ -41,14 +41,10 @@ module OmniContacts
41
41
  # creating nil fields to keep the fields consistent across other networks
42
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
43
  contact[:id] = entry['user_id'] ? entry['user_id'] : entry['id']
44
- if valid_email? entry["name"]
45
- contact[:email] = entry["name"]
46
- contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:email])
47
- else
48
- contact[:first_name] = normalize_name(entry['first_name'])
49
- contact[:last_name] = normalize_name(entry['last_name'])
50
- contact[:name] = normalize_name(entry['name'])
51
- end
44
+ contact[:email] = parse_email(entry['emails']) if valid_email? parse_email(entry['emails'])
45
+ contact[:first_name] = normalize_name(entry['first_name'])
46
+ contact[:last_name] = normalize_name(entry['last_name'])
47
+ contact[:name] = normalize_name(entry['name'])
52
48
  contact[:birthday] = birthday_format(entry['birth_month'], entry['birth_day'], entry['birth_year'])
53
49
  contact[:gender] = entry['gender']
54
50
  contact[:profile_picture] = image_url(entry['user_id'])
@@ -60,7 +56,7 @@ module OmniContacts
60
56
 
61
57
  def parse_email(emails)
62
58
  return nil if emails.nil?
63
- emails['account']
59
+ emails['account'] || emails['preferred'] || emails['personal'] || emails['business'] || emails['other']
64
60
  end
65
61
 
66
62
  def current_user me
@@ -0,0 +1,112 @@
1
+ require "omnicontacts/middleware/oauth2"
2
+ require "omnicontacts/parse_utils"
3
+ require "json"
4
+
5
+ # API Docs: https://msdn.microsoft.com/en-us/office/office365/api/api-catalog#Outlookcontacts
6
+ module OmniContacts
7
+ module Importer
8
+ class Outlook < Middleware::OAuth2
9
+ include ParseUtils
10
+
11
+ attr_reader :auth_host, :authorize_path, :auth_token_path, :scope
12
+
13
+ def initialize app, client_id, client_secret, options ={}
14
+ super app, client_id, client_secret, options
15
+ @auth_host = "login.microsoftonline.com"
16
+ @authorize_path = "/common/oauth2/v2.0/authorize"
17
+ @scope = options[:permissions] || "https://outlook.office.com/contacts.read"
18
+ @auth_token_path = "/common/oauth2/v2.0/token"
19
+ @contacts_host = "outlook.office.com"
20
+ @contacts_path = "/api/v2.0/me/contacts"
21
+ @self_path = "/api/v2.0/me"
22
+ end
23
+
24
+ def fetch_contacts_using_access_token access_token, token_type
25
+ fetch_current_user(access_token, token_type)
26
+ contacts_response = https_get(@contacts_host, @contacts_path, {}, contacts_req_headers(access_token, token_type))
27
+ contacts_from_response contacts_response
28
+ end
29
+
30
+ def fetch_current_user access_token, token_type
31
+ self_response = https_get(@contacts_host, @self_path, {}, contacts_req_headers(access_token, token_type))
32
+ user = current_user self_response
33
+ set_current_user user
34
+ end
35
+
36
+ private
37
+
38
+ def contacts_req_headers token, token_type
39
+ { "Authorization" => "#{token_type} #{token}" }
40
+ end
41
+
42
+ def current_user me
43
+ return nil if me.nil?
44
+ me = JSON.parse(me)
45
+
46
+ name_splitted = me["DisplayName"].split(" ")
47
+ first_name = name_splitted.first
48
+ last_name = name_splitted.last if name_splitted.size > 1
49
+
50
+ user = empty_contact
51
+ user[:id] = me["Id"]
52
+ user[:email] = me["EmailAddress"]
53
+ user[:name] = me["DisplayName"]
54
+ user[:first_name] = normalize_name(first_name)
55
+ user[:last_name] = normalize_name(last_name)
56
+ user
57
+ end
58
+
59
+ def contacts_from_response response_as_json
60
+ response = JSON.parse(response_as_json)
61
+ contacts = []
62
+ response["value"].each do |entry|
63
+ contact = empty_contact
64
+ # Full fields reference:
65
+ # https://msdn.microsoft.com/office/office365/api/complex-types-for-mail-contacts-calendar#RESTAPIResourcesContact
66
+ contact[:id] = entry["Id"]
67
+ contact[:first_name] = entry["GivenName"]
68
+ contact[:last_name] = entry["Surname"]
69
+ contact[:name] = entry["DisplayName"]
70
+ contact[:email] = parse_email(entry["EmailAddresses"])
71
+ contact[:birthday] = birthday(entry["Birthday"])
72
+
73
+ address = [entry["HomeAddress"], entry["BusinessAddress"], entry["OtherAddress"]].reject(&:empty?).first
74
+ if address
75
+ contact[:address_1] = address["Street"]
76
+ contact[:city] = address["City"]
77
+ contact[:region] = address["State"]
78
+ contact[:postcode] = address["PostalCode"]
79
+ contact[:country] = address["CountryOrRegion"]
80
+ end
81
+
82
+ contacts << contact if contact[:name] || contact[:first_name]
83
+ end
84
+ contacts
85
+ end
86
+
87
+ def empty_contact
88
+ { :id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil,
89
+ :gender => nil, :birthday => nil, :profile_picture => nil, :address_1 => nil,
90
+ :address_2 => nil, :city => nil, :region => nil, :postcode => nil, :relation => nil }
91
+ end
92
+
93
+ def parse_email emails
94
+ return nil if emails.nil?
95
+ emails.map! { |email| email["Address"] }
96
+ emails.select! { |email| valid_email? email }
97
+ emails.first
98
+ end
99
+
100
+ def birthday dob
101
+ return nil if dob.nil?
102
+ birthday = dob[0..9].split("-")
103
+ birthday[0] = nil if birthday[0].to_i < 1900 # if year is not set it returns 1604
104
+ return birthday_format(birthday[1], birthday[2], birthday[0])
105
+ end
106
+
107
+ def valid_email? value
108
+ /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/.match(value)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -2,28 +2,30 @@ require 'singleton'
2
2
 
3
3
  class IntegrationTest
4
4
  include Singleton
5
-
5
+
6
6
  attr_accessor :enabled
7
-
7
+
8
8
  def initialize
9
9
  enabled = false
10
10
  clear_mocks
11
11
  end
12
-
12
+
13
13
  def clear_mocks
14
- @mock = {}
14
+ @user_mocks = {}
15
+ @contact_mocks = {}
15
16
  end
16
-
17
- def mock provider, mock
18
- @mock[provider.to_sym] = mock
17
+
18
+ def mock provider, contacts, user = {}
19
+ @contact_mocks[provider.to_sym] = contacts
20
+ @user_mocks[provider.to_sym] = user
19
21
  end
20
-
22
+
21
23
  def mock_authorization_from_user provider
22
24
  [302, {"Content-Type" => "application/x-www-form-urlencoded", "location" => provider.redirect_path}, []]
23
25
  end
24
-
26
+
25
27
  def mock_fetch_contacts provider
26
- result = @mock[provider.class_name.to_sym] || []
28
+ result = @contact_mocks[provider.class_name.to_sym] || []
27
29
  if result.is_a? Array
28
30
  result
29
31
  elsif result.is_a? Hash
@@ -32,5 +34,8 @@ class IntegrationTest
32
34
  raise result.to_s
33
35
  end
34
36
  end
35
-
36
- end
37
+
38
+ def mock_fetch_user provider
39
+ @user_mocks[provider.class_name.to_sym] || {}
40
+ end
41
+ end
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Extending classes are required to implement
5
5
  # the following methods:
6
- # * request_authorization_from_user
6
+ # * request_authorization_from_user
7
7
  # * fetch_contatcs
8
8
  module OmniContacts
9
9
  module Middleware
@@ -22,9 +22,9 @@ module OmniContacts
22
22
  end
23
23
 
24
24
  # Rack callback. It handles three cases:
25
- # * user visit middleware entry point.
25
+ # * user visit middleware entry point.
26
26
  # In this case request_authorization_from_user is called
27
- # * user is redirected back to the application
27
+ # * user is redirected back to the application
28
28
  # from the authorization site. In this case the list
29
29
  # of contacts is fetched and stored in the variables
30
30
  # omnicontacts.contacts within the Rack env variable.
@@ -34,8 +34,10 @@ module OmniContacts
34
34
  def call env
35
35
  @env = env
36
36
  if env["PATH_INFO"] =~ /^#{@listening_path}\/?$/
37
+ session['omnicontacts.params'] = Rack::Request.new(env).params
37
38
  handle_initial_request
38
39
  elsif env["PATH_INFO"] =~ /^#{redirect_path}/
40
+ env['omnicontacts.params'] = session.delete('omnicontacts.params')
39
41
  handle_callback
40
42
  else
41
43
  @app.call(env)
@@ -43,7 +45,7 @@ module OmniContacts
43
45
  end
44
46
 
45
47
  private
46
-
48
+
47
49
  def test_mode?
48
50
  IntegrationTest.instance.enabled
49
51
  end
@@ -65,6 +67,7 @@ module OmniContacts
65
67
  else
66
68
  fetch_contacts
67
69
  end
70
+ set_current_user IntegrationTest.instance.mock_fetch_user(self) if test_mode?
68
71
  @app.call(@env)
69
72
  end
70
73
  end
@@ -74,7 +77,7 @@ module OmniContacts
74
77
  end
75
78
 
76
79
  # This method rescues executes a block of code and
77
- # rescue all exceptions. In case of an exception the
80
+ # rescue all exceptions. In case of an exception the
78
81
  # user is redirected to the failure endpoint.
79
82
  def execute_and_rescue_exceptions
80
83
  yield
@@ -89,7 +92,8 @@ module OmniContacts
89
92
  def handle_error error_type, exception
90
93
  logger.puts("Error #{error_type} while processing #{@env["PATH_INFO"]}: #{exception.message}") if logger
91
94
  failure_url = "#{ MOUNT_PATH }failure?error_message=#{error_type}&importer=#{class_name}"
92
- target_url = append_state_query(failure_url)
95
+ params_url = append_request_params(failure_url)
96
+ target_url = append_state_query(params_url)
93
97
  [302, {"Content-Type" => "text/html", "location" => target_url}, []]
94
98
  end
95
99
 
@@ -106,9 +110,17 @@ module OmniContacts
106
110
  "omnicontacts." + class_name
107
111
  end
108
112
 
113
+ def append_request_params(target_url)
114
+ return target_url unless @env['omnicontacts.params']
115
+ params = Rack::Utils.build_query(@env['omnicontacts.params'])
116
+ unless params.nil? or params.empty?
117
+ target_url = target_url + (target_url.include?("?")?"&":"?") + params
118
+ end
119
+ return target_url
120
+ end
121
+
109
122
  def append_state_query(target_url)
110
123
  state = Rack::Utils.parse_query(@env['QUERY_STRING'])['state']
111
-
112
124
  unless state.nil?
113
125
  target_url = target_url + (target_url.include?("?")?"&":"?") + 'state=' + state
114
126
  end
@@ -4,7 +4,7 @@ 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', 'Jordan Lance']
7
+ gem.authors = ['Diego Castorina', 'Jordan Lance', 'Asma Tameem', 'Randy Villanueva']
8
8
  gem.email = ['diegocastorina@gmail.com', 'voorruby@gmail.com']
9
9
 
10
10
  gem.add_runtime_dependency 'rack'
@@ -2,11 +2,23 @@ require "spec_helper"
2
2
  require "omnicontacts/importer/gmail"
3
3
 
4
4
  describe OmniContacts::Importer::Gmail do
5
-
6
5
  let(:gmail) { OmniContacts::Importer::Gmail.new({}, "client_id", "client_secret") }
7
6
 
8
- let(:gmail_with_scope_args) { OmniContacts::Importer::Gmail.new({}, "client_id", "client_secret", {scope: "https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/contacts.readonly"}) }
9
-
7
+ let(:gmail_with_scope_args) {
8
+ OmniContacts::Importer::Gmail.new(
9
+ {},
10
+ "client_id",
11
+ "client_secret",
12
+ {
13
+ scope: %w(
14
+ https://www.googleapis.com/auth/contacts.readonly
15
+ https://www.googleapis.com/auth/userinfo#email
16
+ https://www.googleapis.com/auth/userinfo.profile
17
+ ).join(" ")
18
+ }
19
+ )
20
+ }
21
+
10
22
  let(:self_response) {
11
23
  '{
12
24
  "id":"16482944006464829443",
@@ -39,6 +51,8 @@ describe OmniContacts::Importer::Gmail do
39
51
 
40
52
  "title":{"$t":"Users\'s Contacts"},
41
53
  "link":[
54
+ {"rel":"http://schemas.google.com/contacts/2008/rel#photo","type":"image/*",
55
+ "href":"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc","gd$etag":"\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\""},
42
56
  {"rel":"alternate","type":"text/html","href":"http://www.google.com/"},
43
57
  {"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full"},
44
58
  {"rel":"http://schemas.google.com/g/2005#post","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full"},
@@ -60,7 +74,8 @@ describe OmniContacts::Importer::Gmail do
60
74
  "category":[{"scheme":"http://schemas.google.com/g/2005#kind","term":"http://schemas.google.com/contact/2008#contact"}],
61
75
  "title":{"$t":"Edward Bennet"},
62
76
  "link":[
63
- {"rel":"http://schemas.google.com/contacts/2008/rel#photo","type":"image/*","href":"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/1"},
77
+ {"rel":"http://schemas.google.com/contacts/2008/rel#photo","type":"image/*",
78
+ "href":"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc", "gd$etag":"\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\""},
64
79
  {"rel":"self","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1"},
65
80
  {"rel":"edit","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1"}
66
81
  ],
@@ -127,9 +142,9 @@ describe OmniContacts::Importer::Gmail do
127
142
  contacts_as_json
128
143
  end
129
144
  gmail.fetch_contacts_using_access_token token, token_type
130
-
131
- gmail.scope.should eq "https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
132
- gmail_with_scope_args.scope.should eq "https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/contacts.readonly"
145
+
146
+ gmail.scope.should eq "https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
147
+ gmail_with_scope_args.scope.should eq "https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
133
148
  end
134
149
 
135
150
  it "should correctly parse id, name, email, gender, birthday, profile picture and relation for 1st contact" do
@@ -144,9 +159,9 @@ describe OmniContacts::Importer::Gmail do
144
159
  result.first[:name].should eq("Edward Bennet")
145
160
  result.first[:email].should eq("bennet@gmail.com")
146
161
  result.first[:gender].should eq("male")
147
- result.first[:birthday].should eq({:day=>02, :month=>07, :year=>1954})
162
+ result.first[:birthday].should eq({ :day => 02, :month => 07, :year => 1954 })
148
163
  result.first[:relation].should eq('father')
149
- result.first[:profile_picture].should eq("https://profiles.google.com/s2/photos/profile/bennet")
164
+ result.first[:profile_picture].should eq("https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc?&access_token=token")
150
165
  result.first[:dates][0][:name].should eq("anniversary")
151
166
  end
152
167
 
@@ -161,8 +176,8 @@ describe OmniContacts::Importer::Gmail do
161
176
  result.last[:name].should eq("Emilia Fox")
162
177
  result.last[:email].should eq("emilia.fox@gmail.com")
163
178
  result.last[:gender].should eq("female")
164
- result.last[:birthday].should eq({:day=>10, :month=>02, :year=>1974})
165
- result.last[:profile_picture].should eq("https://profiles.google.com/s2/photos/profile/emilia.fox")
179
+ result.last[:birthday].should eq({ :day => 10, :month => 02, :year => 1974 })
180
+ result.last[:profile_picture].should be_nil
166
181
  result.last[:relation].should eq('spouse')
167
182
  result.first[:address_1].should eq('1313 Trashview Court')
168
183
  result.first[:address_2].should eq('Apt. 13')
@@ -187,8 +202,122 @@ describe OmniContacts::Importer::Gmail do
187
202
  user[:name].should eq("Chris Johnson")
188
203
  user[:email].should eq("chrisjohnson@gmail.com")
189
204
  user[:gender].should eq("male")
190
- user[:birthday].should eq({:day=>21, :month=>06, :year=>1982})
191
- user[:profile_picture].should eq("https://profiles.google.com/s2/photos/profile/16482944006464829443")
205
+ user[:birthday].should eq({ :day => 21, :month => 06, :year => 1982 })
206
+ user[:profile_picture].should eq("https://lh3.googleusercontent.com/-b8aFbTBM/AAAAAAI/IWA/vsek/photo.jpg")
207
+ end
208
+
209
+ context "when address_1 is nil" do
210
+ let(:contacts_as_json) {
211
+ '{"version":"1.0","encoding":"UTF-8",
212
+ "feed":{
213
+ "xmlns":"http://www.w3.org/2005/Atom",
214
+ "xmlns$openSearch":"http://a9.com/-/spec/opensearch/1.1/",
215
+ "xmlns$gContact":"http://schemas.google.com/contact/2008",
216
+ "xmlns$batch":"http://schemas.google.com/gdata/batch",
217
+ "xmlns$gd":"http://schemas.google.com/g/2005",
218
+ "gd$etag":"W/\"C0YHRno7fSt7I2A9WhBSQ0Q.\"",
219
+
220
+ "id":{"$t":"logged_in_user@gmail.com"},
221
+ "updated":{"$t":"2013-02-20T20:12:17.405Z"},
222
+ "category":[{
223
+ "scheme":"http://schemas.google.com/g/2005#kind",
224
+ "term":"http://schemas.google.com/contact/2008#contact"
225
+ }],
226
+
227
+ "title":{"$t":"Users\'s Contacts"},
228
+ "link":[
229
+ {"rel":"http://schemas.google.com/contacts/2008/rel#photo","type":"image/*",
230
+ "href":"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc","gd$etag":"\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\""},
231
+ {"rel":"alternate","type":"text/html","href":"http://www.google.com/"},
232
+ {"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full"},
233
+ {"rel":"http://schemas.google.com/g/2005#post","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full"},
234
+ {"rel":"http://schemas.google.com/g/2005#batch","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/batch"},
235
+ {"rel":"self","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full?alt\u003djson\u0026max-results\u003d1"},
236
+ {"rel":"next","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full?alt\u003djson\u0026start-index\u003d2\u0026max-results\u003d1"}
237
+ ],
238
+ "author":[{"name":{"$t":"Edward"},"email":{"$t":"logged_in_user@gmail.com"}}],
239
+ "generator":{"version":"1.0","uri":"http://www.google.com/m8/feeds","$t":"Contacts"},
240
+ "openSearch$totalResults":{"$t":"1007"},
241
+ "openSearch$startIndex":{"$t":"1"},
242
+ "openSearch$itemsPerPage":{"$t":"1"},
243
+ "entry":[
244
+ {
245
+ "gd$etag":"\"R3oyfDVSLyt7I2A9WhBTSEULRA0.\"",
246
+ "id":{"$t":"http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1"},
247
+ "updated":{"$t":"2013-02-14T22:36:36.494Z"},
248
+ "app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2013-02-14T22:36:36.494Z"},
249
+ "category":[{"scheme":"http://schemas.google.com/g/2005#kind","term":"http://schemas.google.com/contact/2008#contact"}],
250
+ "title":{"$t":"Edward Bennet"},
251
+ "link":[
252
+ {"rel":"http://schemas.google.com/contacts/2008/rel#photo","type":"image/*",
253
+ "href":"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc", "gd$etag":"\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\""},
254
+ {"rel":"self","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1"},
255
+ {"rel":"edit","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1"}
256
+ ],
257
+ "gd$name":{
258
+ "gd$fullName":{"$t":"Edward Bennet"},
259
+ "gd$givenName":{"$t":"Edward"},
260
+ "gd$familyName":{"$t":"Bennet"}
261
+ },
262
+ "gd$organization":[{"rel":"http://schemas.google.com/g/2005#other","gd$orgName":{"$t":"Google"},"gd$orgTitle":{"$t":"Master Developer"}}],
263
+ "gContact$birthday":{"when":"1954-07-02"},
264
+ "gContact$relation":{"rel":"father"},
265
+ "gContact$gender":{"value":"male"},
266
+ "gContact$event":[{"rel":"anniversary","gd$when":{"startTime":"1983-04-21"}},{"label":"New Job","gd$when":{"startTime":"2014-12-01"}}],
267
+ "gd$email":[{"rel":"http://schemas.google.com/g/2005#other","address":"bennet@gmail.com","primary":"true"}],
268
+ "gContact$groupMembershipInfo":[{"deleted":"false","href":"http://www.google.com/m8/feeds/groups/logged_in_user%40gmail.com/base/6"}],
269
+ "gd$structuredPostalAddress":[{"rel":"http://schemas.google.com/g/2005#home","gd$formattedAddress":{},"gd$street":{},"gd$postcode":{"$t":"66666"},"gd$country":{"code":"VA","$t":"Valoran"},"gd$city":{"$t":"Nowheresville"},"gd$region":{"$t":"OK"}}],
270
+ "gd$phoneNumber":[{"rel":"http://schemas.google.com/g/2005#mobile","uri":"tel:+34-653-15-76-88","$t":"653157688"}]
271
+ },
272
+ {
273
+ "gd$etag":"\"R3oyfDVSLyt7I2A9WhBTSEULRA0.\"",
274
+ "id":{"$t":"http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1"},
275
+ "updated":{"$t":"2013-02-15T22:36:36.494Z"},
276
+ "app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2013-02-15T22:36:36.494Z"},
277
+ "category":[{"scheme":"http://schemas.google.com/g/2005#kind","term":"http://schemas.google.com/contact/2008#contact"}],
278
+ "title":{"$t":"Emilia Fox"},
279
+ "link":[
280
+ {"rel":"http://schemas.google.com/contacts/2008/rel#photo","type":"image/*","href":"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/1"},
281
+ {"rel":"self","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1"},
282
+ {"rel":"edit","type":"application/atom+xml","href":"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1"}
283
+ ],
284
+ "gd$name":{
285
+ "gd$fullName":{"$t":"Emilia Fox"},
286
+ "gd$givenName":{"$t":"Emilia"},
287
+ "gd$familyName":{"$t":"Fox"}
288
+ },
289
+ "gContact$birthday":{"when":"1974-02-10"},
290
+ "gContact$relation":[{"rel":"spouse"}],
291
+ "gContact$gender":{"value":"female"},
292
+ "gd$email":[{"rel":"http://schemas.google.com/g/2005#other","address":"emilia.fox@gmail.com","primary":"true"}],
293
+ "gContact$groupMembershipInfo":[{"deleted":"false","href":"http://www.google.com/m8/feeds/groups/logged_in_user%40gmail.com/base/6"}]
294
+ }]
295
+ }
296
+ }'
297
+ }
298
+
299
+ it "should correctly parse id, name, email, gender, birthday, profile picture, snailmail address, phone and relation for 2nd contact" do
300
+ gmail.should_receive(:https_get)
301
+ gmail.should_receive(:https_get).and_return(contacts_as_json)
302
+ result = gmail.fetch_contacts_using_access_token token, token_type
303
+ result.size.should be(2)
304
+ result.last[:id].should eq('http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1')
305
+ result.last[:first_name].should eq('Emilia')
306
+ result.last[:last_name].should eq('Fox')
307
+ result.last[:name].should eq("Emilia Fox")
308
+ result.last[:email].should eq("emilia.fox@gmail.com")
309
+ result.last[:gender].should eq("female")
310
+ result.last[:birthday].should eq({ :day => 10, :month => 02, :year => 1974 })
311
+ result.last[:profile_picture].should be_nil
312
+ result.last[:relation].should eq('spouse')
313
+ result.first[:address_1].should eq(nil)
314
+ result.first[:address_2].should eq(nil)
315
+ result.first[:city].should eq('Nowheresville')
316
+ result.first[:region].should eq('OK')
317
+ result.first[:country].should eq('VA')
318
+ result.first[:postcode].should eq('66666')
319
+ result.first[:phone_number].should eq('653157688')
320
+ end
192
321
  end
193
322
  end
194
- end
323
+ end
@@ -0,0 +1,150 @@
1
+ require "spec_helper"
2
+ require "omnicontacts/importer/outlook"
3
+
4
+ describe OmniContacts::Importer::Outlook do
5
+
6
+ let(:permissions) { "Contacts.Read" }
7
+ let(:outlook) { OmniContacts::Importer::Outlook.new({}, "app_id", "app_secret", {:permissions => permissions}) }
8
+
9
+ let(:self_response) {
10
+ '{
11
+ "@odata.context": "https://outlook.office.com/api/v2.0/$metadata#Me",
12
+ "@odata.id": "https://outlook.office.com/api/v2.0/Users(\'00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa\')",
13
+ "Id": "00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa",
14
+ "EmailAddress": "test.user@outlook.com",
15
+ "DisplayName": "Test User",
16
+ "Alias": "puid-00034001DF52D3D5",
17
+ "MailboxGuid": "00034001-df52-d3d5-0000-000000000000"
18
+ }'
19
+ }
20
+
21
+ let(:contacts_as_json) {
22
+ '{
23
+ "@odata.context": "https://outlook.office.com/api/v2.0/$metadata#Me/Contacts",
24
+ "value": [{
25
+ "@odata.id": "https://outlook.office.com/api/v2.0/Users(\'00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa\')/Contacts(\'AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgBGAAADQ1hAWLJpwk6DZYyOhnclvgcAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAAxCL3G7jnpkiRUVmiNrhjJgAAAixsAAAA\')",
26
+ "@odata.etag": "W/\"EQAAABYAAADEIvcbuOemSJFRWaI2uGMmAAAAlo4t\"",
27
+ "Id": "AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgBGAAADQ1hAWLJpwk6DZYyOhnclvgcAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAAxCL3G7jnpkiRUVmiNrhjJgAAAixsAAAA",
28
+ "CreatedDateTime": "2016-04-13T21:25:24Z",
29
+ "LastModifiedDateTime": "2016-04-14T19:36:55Z",
30
+ "ChangeKey": "EQAAABYAAADEIvcbuOemSJFRWaI2uGMmAAAAlo4t",
31
+ "Categories": [],
32
+ "ParentFolderId": "AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgAuAAADQ1hAWLJpwk6DZYyOhnclvgEAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAA",
33
+ "Birthday": "1604-08-14T00:00:00Z",
34
+ "FileAs": "Contact, First",
35
+ "DisplayName": "First Contact",
36
+ "GivenName": "First",
37
+ "Initials": null,
38
+ "MiddleName": null,
39
+ "NickName": null,
40
+ "Surname": "Contact",
41
+ "Title": null,
42
+ "YomiGivenName": null,
43
+ "YomiSurname": null,
44
+ "YomiCompanyName": null,
45
+ "Generation": null,
46
+ "EmailAddresses": [{
47
+ "Name": "contact.first@email.com",
48
+ "Address": "contact.first@email.com"
49
+ }, {
50
+ "Name": "contact.second@email.com",
51
+ "Address": "contact.second@email.com"
52
+ }],
53
+ "ImAddresses": [],
54
+ "JobTitle": null,
55
+ "CompanyName": null,
56
+ "Department": null,
57
+ "OfficeLocation": null,
58
+ "Profession": null,
59
+ "BusinessHomePage": null,
60
+ "AssistantName": null,
61
+ "Manager": null,
62
+ "HomePhones": [],
63
+ "MobilePhone1": null,
64
+ "BusinessPhones": [],
65
+ "HomeAddress": {
66
+ "Street": "address1",
67
+ "City": "city",
68
+ "State": "state",
69
+ "CountryOrRegion": "US",
70
+ "PostalCode": "89111"
71
+ },
72
+ "BusinessAddress": {},
73
+ "OtherAddress": {},
74
+ "SpouseName": null,
75
+ "PersonalNotes": null,
76
+ "Children": []
77
+ }]
78
+ }'
79
+ }
80
+
81
+ describe "fetch_contacts_using_access_token" do
82
+
83
+ let(:access_token) { "access_token" }
84
+ let(:token_type) { "token_type" }
85
+
86
+ before(:each) do
87
+ outlook.instance_variable_set(:@env, {"HTTP_HOST" => "http://example.com"})
88
+ end
89
+
90
+ it "should request the contacts by providing the authorization header with token_type and access_token" do
91
+ outlook.should_receive(:https_get) do |host, path, params, headers|
92
+ params.should eq({})
93
+ headers["Authorization"].should eq("token_type access_token")
94
+ self_response
95
+ end
96
+
97
+ outlook.should_receive(:https_get) do |host, path, params, headers|
98
+ params.should eq({})
99
+ headers["Authorization"].should eq("token_type access_token")
100
+ contacts_as_json
101
+ end
102
+ outlook.fetch_contacts_using_access_token access_token, token_type
103
+ end
104
+
105
+ it "should set requested permissions in the authorization url" do
106
+ outlook.authorization_url.should match(/scope=#{Regexp.quote(CGI.escape(permissions))}/)
107
+ end
108
+
109
+ it "should correctly parse id, name and email" do
110
+ outlook.should_receive(:https_get).and_return(self_response)
111
+ outlook.should_receive(:https_get).and_return(contacts_as_json)
112
+ result = outlook.fetch_contacts_using_access_token access_token, token_type
113
+
114
+ result.size.should be(1)
115
+ result.first[:id].should eq('AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgBGAAADQ1hAWLJpwk6DZYyOhnclvgcAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAAxCL3G7jnpkiRUVmiNrhjJgAAAixsAAAA')
116
+ result.first[:first_name].should eq("First")
117
+ result.first[:last_name].should eq("Contact")
118
+ result.first[:name].should eq("First Contact")
119
+ result.first[:email].should eq("contact.first@email.com")
120
+ result.first[:birthday].should eq({ :day => 14, :month => 8, :year => nil })
121
+ result.first[:address_1].should eq("address1")
122
+ result.first[:address_2].should be_nil
123
+ result.first[:city].should eq("city")
124
+ result.first[:region].should eq("state")
125
+ result.first[:postcode].should eq("89111")
126
+ result.first[:country].should eq("US")
127
+ result.first[:gender].should be_nil
128
+ result.first[:profile_picture].should be_nil
129
+ result.first[:relation].should be_nil
130
+ end
131
+
132
+ it "should correctly parse and set logged in user information" do
133
+ outlook.should_receive(:https_get).and_return(self_response)
134
+ outlook.should_receive(:https_get).and_return(contacts_as_json)
135
+
136
+ outlook.fetch_contacts_using_access_token access_token, token_type
137
+
138
+ user = outlook.instance_variable_get(:@env)["omnicontacts.user"]
139
+ user.should_not be_nil
140
+ user[:id].should eq('00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa')
141
+ user[:first_name].should eq("Test")
142
+ user[:last_name].should eq("User")
143
+ user[:name].should eq("Test User")
144
+ user[:email].should eq("test.user@outlook.com")
145
+ user[:gender].should be_nil
146
+ user[:birthday].should be_nil
147
+ end
148
+ end
149
+
150
+ end
@@ -11,7 +11,7 @@ describe IntegrationTest do
11
11
  IntegrationTest.instance.mock_authorization_from_user(provider)[1]["location"].should eq(redirect_path)
12
12
  end
13
13
  end
14
-
14
+
15
15
  context "mock_callback" do
16
16
 
17
17
  before(:each) {
@@ -20,11 +20,11 @@ describe IntegrationTest do
20
20
  @provider.stub(:class_name => "test")
21
21
  IntegrationTest.instance.clear_mocks
22
22
  }
23
-
23
+
24
24
  it "should return an empty contacts list" do
25
25
  IntegrationTest.instance.mock_fetch_contacts(@provider).should be_empty
26
26
  end
27
-
27
+
28
28
  it "should return a configured list of contacts " do
29
29
  contacts = [:name => 'John Doe', :email => 'john@doe.com']
30
30
  IntegrationTest.instance.mock('test', contacts)
@@ -42,10 +42,19 @@ describe IntegrationTest do
42
42
  result.first[:email].should eq(contact[:email])
43
43
  result.first[:name].should eq(contact[:name])
44
44
  end
45
-
45
+
46
+ it "should return a user" do
47
+ contact = {:name => 'John Doe', :email => 'john@doe.com'}
48
+ user = {:name => 'Mary Smith', :email => 'mary@smith.com'}
49
+ IntegrationTest.instance.mock('test', contact, user)
50
+ result = IntegrationTest.instance.mock_fetch_user(@provider)
51
+ result[:email].should eq(user[:email])
52
+ result[:name].should eq(user[:name])
53
+ end
54
+
46
55
  it "should throw an exception" do
47
56
  IntegrationTest.instance.mock('test', :some_error)
48
57
  expect {IntegrationTest.instance.mock_fetch_contacts(@provider)}.to raise_error
49
58
  end
50
59
  end
51
- end
60
+ end
@@ -13,6 +13,14 @@ describe OmniContacts::Middleware::BaseOAuth do
13
13
  def redirect_path
14
14
  "#{ MOUNT_PATH }testprovider/callback"
15
15
  end
16
+
17
+ def self.mock_session
18
+ @mock_session ||= {}
19
+ end
20
+
21
+ def session
22
+ TestProvider.mock_session
23
+ end
16
24
  end
17
25
  OmniContacts.integration_test.enabled = true
18
26
  end
@@ -31,7 +39,7 @@ describe OmniContacts::Middleware::BaseOAuth do
31
39
  last_request.env["omnicontacts.contacts"].first[:email].should eq("user@example.com")
32
40
  end
33
41
 
34
- it "should redurect to failure url" do
42
+ it "should redirect to failure url" do
35
43
  OmniContacts.integration_test.mock(:testprovider, "some_error" )
36
44
  get "#{ MOUNT_PATH }testprovider"
37
45
  get "#{MOUNT_PATH }testprovider/callback"
@@ -44,10 +52,31 @@ describe OmniContacts::Middleware::BaseOAuth do
44
52
  get "#{MOUNT_PATH }testprovider/callback?state=/parent/resource/id"
45
53
  last_response.headers["location"].should eq("#{ MOUNT_PATH }failure?error_message=internal_error&importer=testprovider&state=/parent/resource/id")
46
54
  end
55
+
56
+ it "should store request params in session" do
57
+ OmniContacts.integration_test.mock(:testprovider, :email => "user@example.com")
58
+ get "#{ MOUNT_PATH }testprovider?foo=bar"
59
+ app.session['omnicontacts.params'].should eq({'foo' => 'bar'})
60
+ end
61
+
62
+ it "should pass the params from session to callback environment " do
63
+ OmniContacts.integration_test.mock(:testprovider, :email => "user@example.com")
64
+ app.session.merge!({'omnicontacts.params' => {'foo' => 'bar'}})
65
+ get "#{MOUNT_PATH }testprovider/callback?state=/parent/resource/id"
66
+ last_request.env["omnicontacts.params"].should eq({'foo' => 'bar'})
67
+ end
68
+
69
+ it "should pass the params from session on failure" do
70
+ OmniContacts.integration_test.mock(:testprovider, "some_error" )
71
+ get "#{ MOUNT_PATH }testprovider"
72
+ app.session.merge!({'omnicontacts.params' => {'foo' => 'bar'}})
73
+ get "#{MOUNT_PATH }testprovider/callback"
74
+ last_response.should be_redirect
75
+ last_response.headers["location"].should be_include("foo=bar")
76
+ end
47
77
 
48
78
  after(:all) do
49
79
  OmniContacts.integration_test.enabled = false
50
80
  OmniContacts.integration_test.clear_mocks
51
81
  end
52
-
53
- end
82
+ end
metadata CHANGED
@@ -1,111 +1,100 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnicontacts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
5
- prerelease:
4
+ version: 0.3.10
6
5
  platform: ruby
7
6
  authors:
8
7
  - Diego Castorina
9
8
  - Jordan Lance
9
+ - Asma Tameem
10
+ - Randy Villanueva
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2015-05-21 00:00:00.000000000 Z
14
+ date: 2018-04-23 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: rack
17
18
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
19
  requirements:
20
- - - ! '>='
20
+ - - ">="
21
21
  - !ruby/object:Gem::Version
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
26
  requirements:
28
- - - ! '>='
27
+ - - ">="
29
28
  - !ruby/object:Gem::Version
30
29
  version: '0'
31
30
  - !ruby/object:Gem::Dependency
32
31
  name: json
33
32
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
33
  requirements:
36
- - - ! '>='
34
+ - - ">="
37
35
  - !ruby/object:Gem::Version
38
36
  version: '0'
39
37
  type: :runtime
40
38
  prerelease: false
41
39
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
40
  requirements:
44
- - - ! '>='
41
+ - - ">="
45
42
  - !ruby/object:Gem::Version
46
43
  version: '0'
47
44
  - !ruby/object:Gem::Dependency
48
45
  name: simplecov
49
46
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
47
  requirements:
52
- - - ! '>='
48
+ - - ">="
53
49
  - !ruby/object:Gem::Version
54
50
  version: '0'
55
51
  type: :development
56
52
  prerelease: false
57
53
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
54
  requirements:
60
- - - ! '>='
55
+ - - ">="
61
56
  - !ruby/object:Gem::Version
62
57
  version: '0'
63
58
  - !ruby/object:Gem::Dependency
64
59
  name: rake
65
60
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
61
  requirements:
68
- - - ! '>='
62
+ - - ">="
69
63
  - !ruby/object:Gem::Version
70
64
  version: '0'
71
65
  type: :development
72
66
  prerelease: false
73
67
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
68
  requirements:
76
- - - ! '>='
69
+ - - ">="
77
70
  - !ruby/object:Gem::Version
78
71
  version: '0'
79
72
  - !ruby/object:Gem::Dependency
80
73
  name: rack-test
81
74
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
75
  requirements:
84
- - - ! '>='
76
+ - - ">="
85
77
  - !ruby/object:Gem::Version
86
78
  version: '0'
87
79
  type: :development
88
80
  prerelease: false
89
81
  version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
82
  requirements:
92
- - - ! '>='
83
+ - - ">="
93
84
  - !ruby/object:Gem::Version
94
85
  version: '0'
95
86
  - !ruby/object:Gem::Dependency
96
87
  name: rspec
97
88
  requirement: !ruby/object:Gem::Requirement
98
- none: false
99
89
  requirements:
100
- - - ! '>='
90
+ - - ">="
101
91
  - !ruby/object:Gem::Version
102
92
  version: '0'
103
93
  type: :development
104
94
  prerelease: false
105
95
  version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
96
  requirements:
108
- - - ! '>='
97
+ - - ">="
109
98
  - !ruby/object:Gem::Version
110
99
  version: '0'
111
100
  description: A generalized Rack middleware for importing contacts from major email
@@ -117,7 +106,7 @@ executables: []
117
106
  extensions: []
118
107
  extra_rdoc_files: []
119
108
  files:
120
- - .gitignore
109
+ - ".gitignore"
121
110
  - Gemfile
122
111
  - Gemfile.lock
123
112
  - README.md
@@ -132,6 +121,7 @@ files:
132
121
  - lib/omnicontacts/importer/gmail.rb
133
122
  - lib/omnicontacts/importer/hotmail.rb
134
123
  - lib/omnicontacts/importer/linkedin.rb
124
+ - lib/omnicontacts/importer/outlook.rb
135
125
  - lib/omnicontacts/importer/yahoo.rb
136
126
  - lib/omnicontacts/integration_test.rb
137
127
  - lib/omnicontacts/middleware/base_oauth.rb
@@ -146,6 +136,7 @@ files:
146
136
  - spec/omnicontacts/importer/gmail_spec.rb
147
137
  - spec/omnicontacts/importer/hotmail_spec.rb
148
138
  - spec/omnicontacts/importer/linkedin_spec.rb
139
+ - spec/omnicontacts/importer/outlook_spec.rb
149
140
  - spec/omnicontacts/importer/yahoo_spec.rb
150
141
  - spec/omnicontacts/integration_test_spec.rb
151
142
  - spec/omnicontacts/middleware/base_oauth_spec.rb
@@ -155,26 +146,25 @@ files:
155
146
  - spec/spec_helper.rb
156
147
  homepage: http://github.com/Diego81/omnicontacts
157
148
  licenses: []
149
+ metadata: {}
158
150
  post_install_message:
159
151
  rdoc_options: []
160
152
  require_paths:
161
153
  - lib
162
154
  required_ruby_version: !ruby/object:Gem::Requirement
163
- none: false
164
155
  requirements:
165
- - - ! '>='
156
+ - - ">="
166
157
  - !ruby/object:Gem::Version
167
158
  version: '0'
168
159
  required_rubygems_version: !ruby/object:Gem::Requirement
169
- none: false
170
160
  requirements:
171
- - - ! '>='
161
+ - - ">="
172
162
  - !ruby/object:Gem::Version
173
163
  version: 1.3.6
174
164
  requirements: []
175
165
  rubyforge_project:
176
- rubygems_version: 1.8.23
166
+ rubygems_version: 2.7.4
177
167
  signing_key:
178
- specification_version: 3
168
+ specification_version: 4
179
169
  summary: A generalized Rack middleware for importing contacts from major email providers.
180
170
  test_files: []