linkedin-v2 0.1.3

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/CONTRIBUTING.md +1 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +22 -0
  6. data/README.md +224 -0
  7. data/Rakefile +19 -0
  8. data/lib/linked_in/access_token.rb +24 -0
  9. data/lib/linked_in/api.rb +108 -0
  10. data/lib/linked_in/api_resource.rb +180 -0
  11. data/lib/linked_in/communications.rb +40 -0
  12. data/lib/linked_in/configuration.rb +41 -0
  13. data/lib/linked_in/connection.rb +35 -0
  14. data/lib/linked_in/errors.rb +73 -0
  15. data/lib/linked_in/jobs.rb +11 -0
  16. data/lib/linked_in/mash.rb +68 -0
  17. data/lib/linked_in/media.rb +13 -0
  18. data/lib/linked_in/oauth2.rb +223 -0
  19. data/lib/linked_in/organizations.rb +217 -0
  20. data/lib/linked_in/people.rb +151 -0
  21. data/lib/linked_in/raise_error.rb +28 -0
  22. data/lib/linked_in/search.rb +70 -0
  23. data/lib/linked_in/share_and_social_stream.rb +143 -0
  24. data/lib/linked_in/version.rb +3 -0
  25. data/lib/linkedin-v2.rb +52 -0
  26. data/linkedin-v2.gemspec +39 -0
  27. data/pkg/linkedin-oauth2-2.0.0.gem +0 -0
  28. data/spec/linked_in/api/api_spec.rb +41 -0
  29. data/spec/linked_in/api/communications_spec.rb +13 -0
  30. data/spec/linked_in/api/jobs_spec.rb +33 -0
  31. data/spec/linked_in/api/organizations_spec.rb +54 -0
  32. data/spec/linked_in/api/people_spec.rb +191 -0
  33. data/spec/linked_in/api/search_spec.rb +71 -0
  34. data/spec/linked_in/api/share_and_social_stream_spec.rb +87 -0
  35. data/spec/linked_in/configuration_spec.rb +46 -0
  36. data/spec/linked_in/connection_spec.rb +10 -0
  37. data/spec/linked_in/module_loading_spec.rb +23 -0
  38. data/spec/linked_in/oauth/access_token_spec.rb +27 -0
  39. data/spec/linked_in/oauth/auth_code_spec.rb +86 -0
  40. data/spec/linked_in/oauth/credentials_spec.rb +96 -0
  41. data/spec/linked_in/oauth/get_access_token_spec.rb +108 -0
  42. data/spec/spec_helper.rb +16 -0
  43. data/spec/vcr_cassettes/access_token_success.yml +99 -0
  44. data/spec/vcr_cassettes/bad_code.yml +99 -0
  45. data/spec/vcr_cassettes/organization_data.yml +51 -0
  46. data/spec/vcr_cassettes/people_picture_urls.yml +52 -0
  47. data/spec/vcr_cassettes/people_profile_connections_fields.yml +52 -0
  48. data/spec/vcr_cassettes/people_profile_connections_other.yml +52 -0
  49. data/spec/vcr_cassettes/people_profile_connections_self.yml +52 -0
  50. data/spec/vcr_cassettes/people_profile_fields_complex.yml +52 -0
  51. data/spec/vcr_cassettes/people_profile_fields_simple.yml +52 -0
  52. data/spec/vcr_cassettes/people_profile_lang_spanish.yml +53 -0
  53. data/spec/vcr_cassettes/people_profile_multiple_fields.yml +52 -0
  54. data/spec/vcr_cassettes/people_profile_multiple_uids.yml +52 -0
  55. data/spec/vcr_cassettes/people_profile_multiple_uids_and_urls.yml +52 -0
  56. data/spec/vcr_cassettes/people_profile_multiple_urls.yml +52 -0
  57. data/spec/vcr_cassettes/people_profile_new_connections_fields.yml +52 -0
  58. data/spec/vcr_cassettes/people_profile_new_connections_other.yml +52 -0
  59. data/spec/vcr_cassettes/people_profile_new_connections_self.yml +52 -0
  60. data/spec/vcr_cassettes/people_profile_other_uid.yml +57 -0
  61. data/spec/vcr_cassettes/people_profile_other_url.yml +54 -0
  62. data/spec/vcr_cassettes/people_profile_own.yml +57 -0
  63. data/spec/vcr_cassettes/people_profile_own_secure.yml +53 -0
  64. data/spec/vcr_cassettes/people_profile_skills.yml +52 -0
  65. data/spec/vcr_cassettes/unavailable.yml +99 -0
  66. metadata +285 -0
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe LinkedIn::Search, helpers: :api do
4
+ let(:access_token) {"dummy_access_token"}
5
+ let(:api) {LinkedIn::API.new(access_token)}
6
+
7
+ def stub(url)
8
+ url += "oauth2_access_token=#{access_token}"
9
+ stub_request(:get, url).to_return(body: '{}')
10
+ end
11
+
12
+ it "lets you search for all your contacts" do
13
+ stub("https://api.linkedin.com/v1/people-search?")
14
+ expect(api.search()).to be_kind_of LinkedIn::Mash
15
+ end
16
+
17
+ it "allows a keyword search with no hash" do
18
+ stub("https://api.linkedin.com/v1/people-search?keywords=Proximate%20Harvard&")
19
+ expect(api.search("Proximate Harvard")).to be_kind_of LinkedIn::Mash
20
+ end
21
+
22
+ it "allows a keyword search with the kewywords option" do
23
+ stub("https://api.linkedin.com/v1/people-search?keywords=Proximate%20Harvard&")
24
+ expect(api.search(keywords: "Proximate Harvard")).to be_kind_of LinkedIn::Mash
25
+ end
26
+
27
+ it "lets you search by an attribute" do
28
+ stub("https://api.linkedin.com/v1/people-search?school-name=Olin%20College%20of%20Engineering&")
29
+ expect(api.search(school_name: "Olin College of Engineering")).to be_kind_of LinkedIn::Mash
30
+ end
31
+
32
+ it "combines searches" do
33
+ stub("https://api.linkedin.com/v1/people-search?first-name=Evan&last-name=Morikawa&")
34
+ expect(api.search(first_name: "Evan", last_name: "Morikawa")).to be_kind_of LinkedIn::Mash
35
+ end
36
+
37
+ it "searches for specific fields" do
38
+ stub("https://api.linkedin.com/v1/people-search:(people:(id,first-name,last-name),num-results)?")
39
+ expect(api.search(fields: [{people: ["id", "first-name", "last-name"]}, "num-results"])).to be_kind_of LinkedIn::Mash
40
+ end
41
+
42
+ it "allows you to pass a sort parameter" do
43
+ stub("https://api.linkedin.com/v1/people-search?sort=connections&")
44
+ expect(api.search(sort: "connections")).to be_kind_of LinkedIn::Mash
45
+ end
46
+
47
+ it "it allows start and count parameters" do
48
+ stub("https://api.linkedin.com/v1/people-search?start=10&count=5&")
49
+ expect(api.search(start: 10, count: 5)).to be_kind_of LinkedIn::Mash
50
+ end
51
+
52
+ it "it lets you search by facets" do
53
+ stub("https://api.linkedin.com/v1/people-search:(facets:(code,buckets:(code,name)))?facets=location&")
54
+
55
+ fields = {facets: ["code", {buckets: ["code", "name"]}]}
56
+
57
+ expect(api.search(fields: fields,
58
+ facets: "location")).to be_kind_of LinkedIn::Mash
59
+ end
60
+
61
+ it "lets you search for complex facets" do
62
+ stub("https://api.linkedin.com/v1/people-search:(facets:(code,buckets:(code,name,count)))?facets=location,network&facet=location,us:84&facet=network,F&")
63
+
64
+ fields = {facets: ["code", {buckets: ["code", "name", "count"]}]}
65
+
66
+ expect(api.search(fields: fields,
67
+ facets: "location,network",
68
+ facet: ["location,us:84", "network,F"])
69
+ ).to be_kind_of LinkedIn::Mash
70
+ end
71
+ end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe LinkedIn::ShareAndSocialStream do
4
+ let(:access_token) { "dummy_access_token" }
5
+ let(:api) { LinkedIn::API.new(access_token) }
6
+
7
+ def stub(url)
8
+ stub_request(:get, url).to_return(body: '{}')
9
+ end
10
+
11
+ # no longer supported in v2?
12
+ # it "should be able to view network_updates" do
13
+ # stub("https://api.linkedin.com/v1/people/~/network/updates?")
14
+ # expect(api.network_updates).to be_an_instance_of(LinkedIn::Mash)
15
+ # end
16
+
17
+ # it "should be able to view network_update's comments" do
18
+ # stub("https://api.linkedin.com/v1/people/~/network/updates/key=network_update_key/update-comments?")
19
+ # expect(api.share_comments("network_update_key")).to be_an_instance_of(LinkedIn::Mash)
20
+ # end
21
+
22
+ it "should be able to view comment's likes" do
23
+ stub('https://api.linkedin.com/v2/socialActions/urn:li:comment:123/likes')
24
+ expect(api.likes(urn: 'urn:li:comment:123')).to be_an_instance_of(LinkedIn::Mash)
25
+ end
26
+
27
+ it "should be able to share a new status" do
28
+ stub_request(:post, 'https://api.linkedin.com/v2/shares').to_return(body: '', status: 201)
29
+ response = api.share(comment: 'Testing, 1, 2, 3')
30
+ expect(response.status).to eq 201
31
+ expect(response.body).to eq ''
32
+ end
33
+
34
+ it "returns the shares for a person" do
35
+ stub('https://api.linkedin.com/v2/shares?after=1234&count=35&owners=&q=owners')
36
+ api.shares(:after => 1234, :count => 35)
37
+ end
38
+
39
+ # it "should be able to comment on network update" do
40
+ # stub_request(:post, "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/update-comments?oauth2_access_token=#{access_token}").to_return(body: "", status: 201)
41
+ # response = api.update_comment('SOMEKEY', "Testing, 1, 2, 3")
42
+ # expect(response.body).to eq ""
43
+ # expect(response.status).to eq 201
44
+ # end
45
+
46
+ it "should be able to like a share" do
47
+ stub_request(:post, 'https://api.linkedin.com/v2/socialActions/urn:li:organization:123/likes')
48
+ .to_return(body: "", status: 201)
49
+ response = api.like(urn: 'urn:li:organization:123', object: 'urn:li:share:456', actor: 'urn:li:person:789')
50
+ expect(response.body).to eq ""
51
+ expect(response.status).to eq 201
52
+ end
53
+
54
+ # it "should be able to unlike a network update" do
55
+ # stub_request(:put, "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/is-liked?oauth2_access_token=#{access_token}").to_return(body: "", status: 201)
56
+ # response = api.unlike_share('SOMEKEY')
57
+ # expect(response.body).to eq ""
58
+ # expect(response.status).to eq 201
59
+ # end
60
+
61
+ context 'throttling' do
62
+ # Not sure what LinkedIn does on a rate limit violation on v2? It's
63
+ # not documented here:
64
+ # https://developer.linkedin.com/docs/guide/v2/concepts/rate-limits
65
+ xit 'throws the right exception' do
66
+ stub_request(:post, "https://api.linkedin.com/v1/people/~/shares?format=json&oauth2_access_token=#{access_token}")
67
+ .to_return(
68
+ body: "{\n \"errorCode\": 0,\n \"message\": \"Throttle limit for calls to this resource is reached.\",\n \"requestId\": \"M784AXE9MJ\",\n \"status\": 403,\n \"timestamp\": 1412871058321\n}",
69
+ status: 403
70
+ )
71
+
72
+ err_msg = LinkedIn::ErrorMessages.throttled
73
+ expect {
74
+ api.share(:comment => 'Testing, 1, 2, 3')
75
+ }.to raise_error(LinkedIn::AccessDeniedError, err_msg)
76
+
77
+ error = nil
78
+ begin
79
+ api.add_share(:comment => "Testing, 1, 2, 3")
80
+ rescue => e
81
+ error = e
82
+ end
83
+
84
+ expect(error.data["status"]).to eq 403
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe "LinkedIn configuration" do
4
+ let(:config_value) { "Foo Bar" }
5
+
6
+ let(:site) { "https://www.linkedin.com" }
7
+ let(:token_url) { "/uas/oauth2/accessToken" }
8
+ let(:authorize_url) { "/uas/oauth2/authorization" }
9
+
10
+ subject { LinkedIn.config }
11
+
12
+ before(:example) do
13
+ LinkedIn.configure do |config|
14
+ config.client_id = config_value
15
+ config.client_secret = config_value
16
+ end
17
+ end
18
+
19
+ it("has a client_id") do
20
+ expect(subject.client_id).to eq config_value
21
+ end
22
+
23
+ it("has a client_secret") do
24
+ expect(subject.client_secret).to eq config_value
25
+ end
26
+
27
+ it("has an aliased api_key") do
28
+ expect(subject.api_key).to eq config_value
29
+ end
30
+
31
+ it("has an aliased secret_key") do
32
+ expect(subject.secret_key).to eq config_value
33
+ end
34
+
35
+ it("has the correct default site") do
36
+ expect(subject.site).to eq site
37
+ end
38
+
39
+ it("has the correct default token_url") do
40
+ expect(subject.token_url).to eq token_url
41
+ end
42
+
43
+ it("has the correct default authorize_url") do
44
+ expect(subject.authorize_url).to eq authorize_url
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ describe LinkedIn::Connection do
2
+ it "inherits from Faraday::Connection" do
3
+ expect(subject).to be_kind_of Faraday::Connection
4
+ end
5
+
6
+ it "has the correct default url" do
7
+ url = LinkedIn.config.api + LinkedIn.config.api_version
8
+ expect(subject.url_prefix.to_s).to eq url
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe LinkedIn do
4
+ it "is a module" do
5
+ expect(LinkedIn).to be_a Module
6
+ end
7
+
8
+ it "is independently loadable" do
9
+ expect { require 'linkedin-v2' }.not_to raise_error
10
+ end
11
+
12
+ describe LinkedIn::OAuth2 do
13
+ it "is a class" do
14
+ expect(LinkedIn::OAuth2).to be_a Class
15
+ end
16
+ end
17
+
18
+ describe LinkedIn::API do
19
+ it "is a class" do
20
+ expect(LinkedIn::API).to be_a Class
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ describe "LinkedIn::AccessToken" do
2
+ let(:tok) {:dummy_access_token}
3
+ let(:expires_in) { 64000 }
4
+ let(:expires_at) { Time.utc(2014,1,1) }
5
+
6
+ context "When just token is set" do
7
+ subject {LinkedIn::AccessToken.new(tok)}
8
+ it("has the token value") { expect(subject.token).to eq tok }
9
+ end
10
+
11
+ context "When only expires_in is set" do
12
+ subject {LinkedIn::AccessToken.new(tok, expires_in)}
13
+ it("set expires_in") {expect(subject.expires_in).to eq expires_in}
14
+ it("calculated expires_at") do
15
+ calc_time = Time.now + expires_in
16
+ diff = subject.expires_at - calc_time
17
+ expect(diff).to be < 1
18
+ end
19
+ end
20
+
21
+ context "When everything is set" do
22
+ subject {LinkedIn::AccessToken.new(tok, expires_in, expires_at)}
23
+ it("set token") { expect(subject.token).to eq tok }
24
+ it("set expires_in") {expect(subject.expires_in).to eq expires_in}
25
+ it("set expires_at") {expect(subject.expires_at).to eq expires_at}
26
+ end
27
+ end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+ require "uri"
3
+ require "cgi"
4
+
5
+ describe "OAuth2 Auth Code" do
6
+ before(:example) do
7
+ LinkedIn.configure do |config|
8
+ config.client_id = "dummy_client_id"
9
+ config.client_secret = "dummy_client_secret"
10
+ end
11
+ end
12
+ subject { LinkedIn::OAuth2.new }
13
+
14
+ def params(key, opts=nil)
15
+ CGI.parse(URI.parse(subject.auth_code_url(opts)).query)[key][0]
16
+ end
17
+
18
+ context "When no redirect_uri is given" do
19
+ before(:example) do
20
+ LinkedIn.configure { |config| config.redirect_uri = nil }
21
+ end
22
+
23
+ let(:err_msg) { LinkedIn::ErrorMessages.redirect_uri }
24
+
25
+ it "Throws an error" do
26
+ expect {subject.auth_code_url}.to raise_error(LinkedIn::InvalidRequest, err_msg)
27
+ end
28
+ end
29
+
30
+ context "When an auth code url is requested with no options" do
31
+ let(:redirect_uri) { "http://lvh.me:5000" }
32
+ let(:scope) { "r_fullprofile r_emailaddress r_network" }
33
+
34
+ before(:example) do
35
+ LinkedIn.configure do |config|
36
+ config.redirect_uri = redirect_uri
37
+ config.scope = scope
38
+ end
39
+ end
40
+
41
+ it "Returns the client id in the uri" do
42
+ expect(params("client_id")).to eq LinkedIn.config.client_id
43
+ end
44
+
45
+ it "Includes the configured redirect_uri" do
46
+ expect(params("redirect_uri")).to eq redirect_uri
47
+ end
48
+
49
+ it "Includes the configured scope" do
50
+ expect(params("scope")).to eq scope
51
+ end
52
+
53
+ it "Includes an autogenerated state" do
54
+ expect(params("state")).to_not be_nil
55
+ end
56
+ end
57
+
58
+ context "When an auth code url is requested with options" do
59
+ let(:state) { "foobarbaz" }
60
+ let(:scope) { "r_basicprofile rw_nus" }
61
+ let(:redirect_uri) { "https://example.com" }
62
+
63
+ let(:opts) do
64
+ {state: state,
65
+ scope: scope,
66
+ redirect_uri: redirect_uri}
67
+ end
68
+
69
+ it "Returns the client id in the uri" do
70
+ expect(params("client_id", opts)).to eq LinkedIn.config.client_id
71
+ end
72
+
73
+ it "Includes the custom redirect_uri" do
74
+ expect(params("redirect_uri", opts)).to eq redirect_uri
75
+ end
76
+
77
+ it "Includes the custom scope" do
78
+ expect(params("scope", opts)).to eq scope
79
+ end
80
+
81
+ it "Includes the custom state" do
82
+ expect(params("state", opts)).to eq state
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,96 @@
1
+ require "spec_helper"
2
+
3
+ describe LinkedIn::OAuth2 do
4
+ let(:site) { LinkedIn.config.site }
5
+ let(:token_url) { LinkedIn.config.token_url }
6
+ let(:authorize_url) { LinkedIn.config.authorize_url }
7
+
8
+ let(:client_id) { "dummy_client_id" }
9
+ let(:client_secret) { "dummy_client_secret" }
10
+
11
+ shared_examples "verify client" do
12
+ it "creates a valid oauth object" do
13
+ expect(subject).to be_kind_of(LinkedIn::OAuth2)
14
+ end
15
+ it "is a subclass of OAuth2::Client" do
16
+ expect(subject).to be_kind_of(OAuth2::Client)
17
+ end
18
+ it "assigned the client_id to id" do
19
+ expect(subject.id).to eq client_id
20
+ end
21
+ it "assigned the client_secret to secret" do
22
+ expect(subject.secret).to eq client_secret
23
+ end
24
+ it "assigned the site to site" do
25
+ expect(subject.site).to eq site
26
+ end
27
+ it "assigned the authorize_url option" do
28
+ expect(subject.options[:authorize_url]).to eq authorize_url
29
+ end
30
+ it "assigned the token_url option" do
31
+ expect(subject.options[:token_url]).to eq token_url
32
+ end
33
+ end
34
+
35
+ shared_examples "options take" do
36
+ it "overrides default options" do
37
+ expect(subject.options[:raise_errors]).to eq false
38
+ end
39
+ it "sets new options" do
40
+ expect(subject.options[:new_opt]).to eq "custom option"
41
+ end
42
+ end
43
+
44
+ context "When client credentials exist" do
45
+ before(:example) do
46
+ LinkedIn.configure do |config|
47
+ config.client_id = client_id
48
+ config.client_secret = client_secret
49
+ end
50
+ end
51
+
52
+ include_examples "verify client"
53
+
54
+ let(:options) do
55
+ return {raise_errors: false,
56
+ new_opt: "custom option"}
57
+ end
58
+
59
+ context "When custom options are passed in as first arg" do
60
+ subject do
61
+ LinkedIn::OAuth2.new(options)
62
+ end
63
+ include_examples "options take"
64
+ end
65
+
66
+ context "When custom options are passed in" do
67
+ subject do
68
+ LinkedIn::OAuth2.new(client_id, client_secret, options)
69
+ end
70
+ include_examples "options take"
71
+ end
72
+
73
+ end
74
+
75
+ context "When client credentials do not exist" do
76
+ let(:err_msg) { LinkedIn::ErrorMessages.credentials_missing }
77
+
78
+ before(:example) do
79
+ LinkedIn.configure do |config|
80
+ config.client_id = nil
81
+ config.client_secret = nil
82
+ end
83
+ end
84
+
85
+ it "raises an error" do
86
+ expect { LinkedIn::OAuth2.new }.to raise_error(LinkedIn::InvalidRequest, err_msg)
87
+ end
88
+ end
89
+
90
+ context "When client credentials are passed in" do
91
+ subject { LinkedIn::OAuth2.new(client_id, client_secret) }
92
+
93
+ include_examples "verify client"
94
+ end
95
+
96
+ end