omnigroupcontacts 0.3.10 → 0.3.11

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +39 -0
  5. data/README.md +132 -0
  6. data/Rakefile +7 -0
  7. data/lib/omnigroupcontacts.rb +19 -0
  8. data/lib/omnigroupcontacts/authorization/oauth1.rb +122 -0
  9. data/lib/omnigroupcontacts/authorization/oauth2.rb +87 -0
  10. data/lib/omnigroupcontacts/builder.rb +30 -0
  11. data/lib/omnigroupcontacts/http_utils.rb +101 -0
  12. data/lib/omnigroupcontacts/importer.rb +5 -0
  13. data/lib/omnigroupcontacts/importer/gmailgroup.rb +238 -0
  14. data/lib/omnigroupcontacts/integration_test.rb +36 -0
  15. data/lib/omnigroupcontacts/middleware/base_oauth.rb +120 -0
  16. data/lib/omnigroupcontacts/middleware/oauth1.rb +70 -0
  17. data/lib/omnigroupcontacts/middleware/oauth2.rb +80 -0
  18. data/lib/omnigroupcontacts/parse_utils.rb +56 -0
  19. data/omnigroupcontacts-0.3.10.gem +0 -0
  20. data/omnigroupcontacts-0.3.8.gem +0 -0
  21. data/omnigroupcontacts-0.3.9.gem +0 -0
  22. data/omnigroupcontacts.gemspec +25 -0
  23. data/spec/omnicontacts/authorization/oauth1_spec.rb +82 -0
  24. data/spec/omnicontacts/authorization/oauth2_spec.rb +92 -0
  25. data/spec/omnicontacts/http_utils_spec.rb +79 -0
  26. data/spec/omnicontacts/importer/facebook_spec.rb +120 -0
  27. data/spec/omnicontacts/importer/gmail_spec.rb +194 -0
  28. data/spec/omnicontacts/importer/hotmail_spec.rb +106 -0
  29. data/spec/omnicontacts/importer/linkedin_spec.rb +67 -0
  30. data/spec/omnicontacts/importer/yahoo_spec.rb +124 -0
  31. data/spec/omnicontacts/integration_test_spec.rb +51 -0
  32. data/spec/omnicontacts/middleware/base_oauth_spec.rb +53 -0
  33. data/spec/omnicontacts/middleware/oauth1_spec.rb +78 -0
  34. data/spec/omnicontacts/middleware/oauth2_spec.rb +67 -0
  35. data/spec/omnicontacts/parse_utils_spec.rb +53 -0
  36. data/spec/spec_helper.rb +12 -0
  37. metadata +37 -2
@@ -0,0 +1,124 @@
1
+ require "spec_helper"
2
+ require "omnigroupcontacts/importer/yahoo"
3
+
4
+ describe OmniGroupContacts::Importer::Yahoo do
5
+
6
+ describe "fetch_contacts_from_token_and_verifier" do
7
+ let(:self_response) {
8
+ '{"profile":{
9
+ "guid":"PCLASP523T3E2R5TFMHDW9KWQQ",
10
+ "birthdate": "06/21",
11
+ "emails":[{"handle":"chrisjohnson@gmail.com", "id":10, "primary":true, "type":"HOME"}, {"handle":"xyz@xyz.com", "id":11, "type":"HOME"}],
12
+ "familyName": "Johnson",
13
+ "gender":"M",
14
+ "givenName":"Chris",
15
+ "image":{"imageUrl":"https://avatars.zenfs.com/users/23T3E2R5TFMHDW-AFE-I7lUpIsGQ==.large.png"}
16
+ }
17
+ }'
18
+ }
19
+
20
+ let(:contacts_as_json) {
21
+ '{
22
+ "contacts": {
23
+ "start":1,
24
+ "count":1,
25
+ "contact":[
26
+ {
27
+ "id":10,
28
+ "fields":[
29
+ {"id":819, "type":"email", "value":"johnny@yahoo.com"},
30
+ {"id":806,"type":"name","value":{"givenName":"John","middleName":"","familyName":"Smith"},"editedBy":"OWNER","categories":[]},
31
+ {"id":33555343,"type":"guid","value":"7ET6MYV2UQ6VR6CBSNMCLFJIVI"},
32
+ {"id":946,"type":"birthday","value":{"day":"22","month":"2","year":"1952"},"editedBy":"OWNER","categories":[]},
33
+ {"id":21, "type":"address", "value":{"street":"1313 Trashview Court\nApt. 13", "city":"Nowheresville", "stateOrProvince":"OK", "postalCode":"66666", "country":"", "countryCode":""}, "editedBy":"OWNER", "flags":["HOME"], "categories":[]}
34
+ ]
35
+ }
36
+ ]
37
+ }
38
+ }' }
39
+
40
+ let(:yahoo) { OmniGroupContacts::Importer::Yahoo.new({}, "consumer_key", "consumer_secret") }
41
+
42
+ before(:each) do
43
+ yahoo.instance_variable_set(:@env, {})
44
+ end
45
+
46
+ it "should request the contacts by specifying all required parameters" do
47
+ yahoo.should_receive(:fetch_access_token).and_return(["access_token", "access_token_secret", "guid"])
48
+
49
+ yahoo.should_receive(:https_get) do |host, path, params|
50
+ params[:format].should eq("json")
51
+ params[:oauth_consumer_key].should eq("consumer_key")
52
+ params[:oauth_nonce].should_not be_nil
53
+ params[:oauth_signature_method].should eq("HMAC-SHA1")
54
+ params[:oauth_timestamp].should_not be_nil
55
+ params[:oauth_token].should eq("access_token")
56
+ params[:oauth_version].should eq("1.0")
57
+ self_response
58
+ end
59
+
60
+ yahoo.should_receive(:https_get) do |host, path, params|
61
+ params[:format].should eq("json")
62
+ params[:oauth_consumer_key].should eq("consumer_key")
63
+ params[:oauth_nonce].should_not be_nil
64
+ params[:oauth_signature_method].should eq("HMAC-SHA1")
65
+ params[:oauth_timestamp].should_not be_nil
66
+ params[:oauth_token].should eq("access_token")
67
+ params[:oauth_version].should eq("1.0")
68
+ contacts_as_json
69
+ end
70
+ yahoo.fetch_contacts_from_token_and_verifier "auth_token", "auth_token_secret", "oauth_verifier"
71
+ end
72
+
73
+ it "should correctly parse id, name,email,gender, birthday, snailmail address, image source and relation for contact and logged in user" do
74
+ yahoo.should_receive(:fetch_access_token).and_return(["access_token", "access_token_secret", "guid"])
75
+ yahoo.should_receive(:https_get).and_return(self_response)
76
+ yahoo.should_receive(:https_get).and_return(contacts_as_json)
77
+ result = yahoo.fetch_contacts_from_token_and_verifier "auth_token", "auth_token_secret", "oauth_verifier"
78
+
79
+ result.size.should be(1)
80
+ result.first[:id].should eq('10')
81
+ result.first[:first_name].should eq('John')
82
+ result.first[:last_name].should eq('Smith')
83
+ result.first[:name].should eq("John Smith")
84
+ result.first[:email].should eq("johnny@yahoo.com")
85
+ result.first[:gender].should be_nil
86
+ result.first[:birthday].should eq({:day=>22, :month=>2, :year=>1952})
87
+ result.first[:address_1].should eq('1313 Trashview Court')
88
+ result.first[:address_2].should eq('Apt. 13')
89
+ result.first[:city].should eq('Nowheresville')
90
+ result.first[:region].should eq('OK')
91
+ result.first[:postcode].should eq('66666')
92
+ result.first[:relation].should be_nil
93
+ end
94
+
95
+ it "should return an empty list of contacts" do
96
+ empty_contacts_list = '{"contacts": {"start":0, "count":0}}'
97
+ yahoo.should_receive(:fetch_access_token).and_return(["access_token", "access_token_secret", "guid"])
98
+ yahoo.should_receive(:https_get).and_return(self_response)
99
+ yahoo.should_receive(:https_get).and_return(empty_contacts_list)
100
+ result = yahoo.fetch_contacts_from_token_and_verifier "auth_token", "auth_token_secret", "oauth_verifier"
101
+
102
+ result.should be_empty
103
+ end
104
+
105
+ it "should correctly parse and set logged in user information" do
106
+ yahoo.should_receive(:fetch_access_token).and_return(["access_token", "access_token_secret", "guid"])
107
+ yahoo.should_receive(:https_get).and_return(self_response)
108
+ yahoo.should_receive(:https_get).and_return(contacts_as_json)
109
+ yahoo.fetch_contacts_from_token_and_verifier "auth_token", "auth_token_secret", "oauth_verifier"
110
+
111
+ user = yahoo.instance_variable_get(:@env)["omnigroupcontacts.user"]
112
+ user.should_not be_nil
113
+ user[:id].should eq('PCLASP523T3E2R5TFMHDW9KWQQ')
114
+ user[:first_name].should eq('Chris')
115
+ user[:last_name].should eq('Johnson')
116
+ user[:name].should eq('Chris Johnson')
117
+ user[:gender].should eq('male')
118
+ user[:birthday].should eq({:day=>21, :month=>06, :year=>nil})
119
+ user[:email].should eq('chrisjohnson@gmail.com')
120
+ user[:profile_picture].should eq('https://avatars.zenfs.com/users/23T3E2R5TFMHDW-AFE-I7lUpIsGQ==.large.png')
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+ require "omnigroupcontacts/integration_test"
3
+
4
+ describe IntegrationTest do
5
+
6
+ context "mock_initial_request" do
7
+ it "should redirect to the provider's redirect_path" do
8
+ provider = mock
9
+ redirect_path = "/redirect_path"
10
+ provider.stub(:redirect_path => redirect_path)
11
+ IntegrationTest.instance.mock_authorization_from_user(provider)[1]["location"].should eq(redirect_path)
12
+ end
13
+ end
14
+
15
+ context "mock_callback" do
16
+
17
+ before(:each) {
18
+ @env = {}
19
+ @provider = self.mock
20
+ @provider.stub(:class_name => "test")
21
+ IntegrationTest.instance.clear_mocks
22
+ }
23
+
24
+ it "should return an empty contacts list" do
25
+ IntegrationTest.instance.mock_fetch_contacts(@provider).should be_empty
26
+ end
27
+
28
+ it "should return a configured list of contacts " do
29
+ contacts = [:name => 'John Doe', :email => 'john@doe.com']
30
+ IntegrationTest.instance.mock('test', contacts)
31
+ result = IntegrationTest.instance.mock_fetch_contacts(@provider)
32
+ result.size.should be(1)
33
+ result.first[:email].should eq(contacts.first[:email])
34
+ result.first[:name].should eq(contacts.first[:name])
35
+ end
36
+
37
+ it "should return a single element list of contacts " do
38
+ contact = {:name => 'John Doe', :email => 'john@doe.com'}
39
+ IntegrationTest.instance.mock('test', contact)
40
+ result = IntegrationTest.instance.mock_fetch_contacts(@provider)
41
+ result.size.should be(1)
42
+ result.first[:email].should eq(contact[:email])
43
+ result.first[:name].should eq(contact[:name])
44
+ end
45
+
46
+ it "should throw an exception" do
47
+ IntegrationTest.instance.mock('test', :some_error)
48
+ expect {IntegrationTest.instance.mock_fetch_contacts(@provider)}.to raise_error
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+ require "omnigroupcontacts"
3
+ require "omnigroupcontacts/middleware/base_oauth"
4
+
5
+ describe OmniGroupContacts::Middleware::BaseOAuth do
6
+
7
+ before(:all) do
8
+ class TestProvider < OmniGroupContacts::Middleware::BaseOAuth
9
+ def initialize app, consumer_key, consumer_secret, options = {}
10
+ super app, options
11
+ end
12
+
13
+ def redirect_path
14
+ "#{ MOUNT_PATH }testprovider/callback"
15
+ end
16
+ end
17
+ omnigroupcontacts.integration_test.enabled = true
18
+ end
19
+
20
+ let(:app) {
21
+ Rack::Builder.new do |b|
22
+ b.use TestProvider, "consumer_id", "consumer_secret"
23
+ b.run lambda { |env| [200, {"Content-Type" => "text/html"}, ["Hello World"]] }
24
+ end.to_app
25
+ }
26
+
27
+ it "should return a preconfigured list of contacts" do
28
+ omnigroupcontacts.integration_test.mock(:testprovider, :email => "user@example.com")
29
+ get "#{ MOUNT_PATH }testprovider"
30
+ get "#{ MOUNT_PATH }testprovider/callback"
31
+ last_request.env["omnigroupcontacts.contacts"].first[:email].should eq("user@example.com")
32
+ end
33
+
34
+ it "should redurect to failure url" do
35
+ omnigroupcontacts.integration_test.mock(:testprovider, "some_error" )
36
+ get "#{ MOUNT_PATH }testprovider"
37
+ get "#{MOUNT_PATH }testprovider/callback"
38
+ last_response.should be_redirect
39
+ last_response.headers["location"].should eq("#{ MOUNT_PATH }failure?error_message=internal_error&importer=testprovider")
40
+ end
41
+
42
+ it "should pass through state query params to the failure url" do
43
+ omnigroupcontacts.integration_test.mock(:testprovider, "some_error" )
44
+ get "#{MOUNT_PATH }testprovider/callback?state=/parent/resource/id"
45
+ last_response.headers["location"].should eq("#{ MOUNT_PATH }failure?error_message=internal_error&importer=testprovider&state=/parent/resource/id")
46
+ end
47
+
48
+ after(:all) do
49
+ omnigroupcontacts.integration_test.enabled = false
50
+ omnigroupcontacts.integration_test.clear_mocks
51
+ end
52
+
53
+ end
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+ require "omnigroupcontacts/middleware/oauth1"
3
+
4
+ describe OmniGroupContacts::Middleware::OAuth1 do
5
+
6
+ before(:all) do
7
+ class OAuth1Middleware < OmniGroupContacts::Middleware::OAuth1
8
+ def self.mock_auth_token_resp
9
+ @mock_auth_token_resp ||= Object.new
10
+ end
11
+
12
+ def fetch_authorization_token
13
+ OAuth1Middleware.mock_auth_token_resp.body
14
+ end
15
+
16
+ def authorization_url auth_token
17
+ "http://www.example.com"
18
+ end
19
+
20
+ def fetch_contacts_from_token_and_verifier oauth_token, ouath_token_secret, oauth_verifier
21
+ [{:name => "John Doe", :email => "john@example.com"}]
22
+ end
23
+
24
+ def self.mock_session
25
+ @mock_session ||= {}
26
+ end
27
+
28
+ def session
29
+ OAuth1Middleware.mock_session
30
+ end
31
+ end
32
+ end
33
+
34
+ let(:app) {
35
+ Rack::Builder.new do |b|
36
+ b.use OAuth1Middleware, "consumer_id", "consumer_secret"
37
+ b.run lambda { |env| [200, {"Content-Type" => "text/html"}, ["Hello World"]] }
38
+ end.to_app
39
+ }
40
+
41
+ context "visiting the listening path" do
42
+ it "should save the authorization token and redirect to the authorization url" do
43
+ OAuth1Middleware.mock_auth_token_resp.should_receive(:body).and_return(["auth_token", "auth_token_secret"])
44
+ get "#{ MOUNT_PATH }oauth1middleware"
45
+ last_response.should be_redirect
46
+ last_response.headers['location'].should eq("http://www.example.com")
47
+ end
48
+
49
+ it "should pass through state query params visiting the listening path" do
50
+ OAuth1Middleware.mock_auth_token_resp.should_receive(:body).and_return(["auth_token", "auth_token_secret"])
51
+ get "#{ MOUNT_PATH }oauth1middleware?state=/parent/resource/id"
52
+ last_response.headers['location'].should eq("http://www.example.com?state=/parent/resource/id")
53
+ end
54
+
55
+ it "should redirect to failure url if fetching the request token does not succeed" do
56
+ OAuth1Middleware.mock_auth_token_resp.should_receive(:body).and_raise("Request failed")
57
+ get "contacts/oauth1middleware"
58
+ last_response.should be_redirect
59
+ last_response.headers["location"].should eq("#{ MOUNT_PATH }failure?error_message=internal_error&importer=oauth1middleware")
60
+ end
61
+ end
62
+
63
+ context "visiting the callback url after authorization" do
64
+ it "should return the list of contacts" do
65
+ OAuth1Middleware.mock_session.should_receive(:[]).and_return("oauth_token_secret")
66
+ get "#{ MOUNT_PATH }oauth1middleware/callback?oauth_token=token&oauth_verifier=verifier"
67
+ last_response.should be_ok
68
+ last_request.env["omnigroupcontacts.contacts"].size.should be(1)
69
+ end
70
+
71
+ it "should redirect to failure url if oauth_token_secret is not found in the session" do
72
+ OAuth1Middleware.mock_session.should_receive(:[]).and_return(nil)
73
+ get "#{ MOUNT_PATH }oauth1middleware/callback?oauth_token=token&oauth_verifier=verifier"
74
+ last_response.should be_redirect
75
+ last_response.headers["location"].should eq("#{ MOUNT_PATH }failure?error_message=not_authorized&importer=oauth1middleware")
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,67 @@
1
+ require "spec_helper"
2
+ require "omnigroupcontacts/middleware/oauth2"
3
+
4
+ describe OmniGroupContacts::Middleware::OAuth2 do
5
+
6
+ before(:all) do
7
+ class OAuth2Middleware < OmniGroupContacts::Middleware::OAuth2
8
+ def authorization_url
9
+ "http://www.example.com"
10
+ end
11
+
12
+ def redirect_path
13
+ "/redirect_path"
14
+ end
15
+
16
+ def self.mock_session
17
+ @mock_session ||= {}
18
+ end
19
+
20
+ def session
21
+ OAuth2Middleware.mock_session
22
+ end
23
+
24
+ def fetch_access_token code
25
+ ["access_token", "token_type", "token_refresh"]
26
+ end
27
+
28
+ def fetch_contacts_using_access_token token, token_type
29
+ [{:name => "John Doe", :email => "john@example.com"}]
30
+ end
31
+ end
32
+ end
33
+
34
+ let(:app) {
35
+ Rack::Builder.new do |b|
36
+ b.use OAuth2Middleware, "client_id", "client_secret"
37
+ b.run lambda { |env| [200, {"Content-Type" => "text/html"}, ["Hello World"]] }
38
+ end.to_app
39
+ }
40
+
41
+ context "visiting the listening path" do
42
+ it "should redirect to authorization site when visiting the listening path" do
43
+ get "#{ MOUNT_PATH }oauth2middleware"
44
+ last_response.should be_redirect
45
+ last_response.headers['location'].should eq("http://www.example.com")
46
+ end
47
+
48
+ it "should pass through state query params visiting the listening path" do
49
+ get "#{ MOUNT_PATH }oauth2middleware?state=/parent/resource/id"
50
+ last_response.headers['location'].should eq("http://www.example.com?state=/parent/resource/id")
51
+ end
52
+ end
53
+
54
+ context "visiting the callback url after authorization" do
55
+ it "should fetch the contacts" do
56
+ get '/redirect_path?code=ABC'
57
+ last_response.should be_ok
58
+ last_request.env["omnigroupcontacts.contacts"].size.should be(1)
59
+ end
60
+
61
+ it "should redirect to failure page because user did not allow access to contacts list" do
62
+ get '/redirect_path?error=not_authorized'
63
+ last_response.should be_redirect
64
+ last_response.headers["location"].should eq("#{ MOUNT_PATH }failure?error_message=not_authorized&importer=oauth2middleware")
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+ require "omnigroupcontacts/parse_utils"
3
+
4
+ include OmniGroupContacts::ParseUtils
5
+
6
+ describe OmniGroupContacts::ParseUtils do
7
+ describe "normalize_name" do
8
+ it "should remove trailing spaces" do
9
+ result = normalize_name("John ")
10
+ result.should eq("John")
11
+ end
12
+
13
+ it "should preserve capitalization" do
14
+ result = normalize_name("John McDonald")
15
+ result.should eq("John McDonald")
16
+ end
17
+ end
18
+
19
+ describe "full_name" do
20
+ it "should preserve capitalization" do
21
+ result = full_name("John", "McDonald")
22
+ result.should eq("John McDonald")
23
+ end
24
+
25
+ it "returns only first name if no last name present" do
26
+ result = full_name("John", nil)
27
+ result.should eq("John")
28
+ end
29
+
30
+ it "returns only last name if no first name present" do
31
+ result = full_name(nil, "McDonald")
32
+ result.should eq("McDonald")
33
+ end
34
+ end
35
+
36
+ describe "birthday_format" do
37
+ it "returns nil if (!year && !month) || (!year && !day)" do
38
+ result = birthday_format(nil, Date.today, nil)
39
+ result.should eq(nil)
40
+
41
+ result = birthday_format(Date.today.month, nil, nil)
42
+ result.should eq(nil)
43
+ end
44
+ end
45
+
46
+ describe "email_to_name" do
47
+ it "create a probable name from email" do
48
+ username_or_email = "foo.bar@test.com"
49
+ result = email_to_name(username_or_email)
50
+ result.should eq ['foo','bar',"foo bar"]
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ require "simplecov"
2
+ SimpleCov.start do
3
+ add_filter "spec/"
4
+ end
5
+
6
+ require "rspec"
7
+ require "rack/test"
8
+ RSpec.configure do |config|
9
+ config.include Rack::Test::Methods
10
+ end
11
+
12
+ MOUNT_PATH = "/contacts/"