linkedin2 0.0.1 → 0.0.2

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/example-linkedin.yml +4 -0
  4. data/lib/linkedin/api.rb +11 -0
  5. data/lib/linkedin/api/authentication.rb +1 -3
  6. data/lib/linkedin/api/companies.rb +14 -0
  7. data/lib/linkedin/api/groups.rb +6 -0
  8. data/lib/linkedin/api/industries.rb +173 -0
  9. data/lib/linkedin/api/jobs.rb +6 -0
  10. data/lib/linkedin/api/permissions.rb +43 -0
  11. data/lib/linkedin/api/profiles.rb +23 -0
  12. data/lib/linkedin/base.rb +28 -0
  13. data/lib/linkedin/client.rb +67 -9
  14. data/lib/linkedin/company.rb +20 -0
  15. data/lib/linkedin/configuration.rb +14 -28
  16. data/lib/linkedin/error.rb +21 -1
  17. data/lib/linkedin/faraday_middleware.rb +10 -0
  18. data/lib/linkedin/faraday_middleware/linkedin_error_response.rb +28 -0
  19. data/lib/linkedin/faraday_middleware/linkedin_format_request.rb +27 -0
  20. data/lib/linkedin/industry.rb +23 -0
  21. data/lib/linkedin/profile.rb +20 -0
  22. data/lib/linkedin/version.rb +1 -1
  23. data/lib/linkedin2.rb +52 -3
  24. data/linkedin.gemspec +5 -0
  25. data/spec/api/companies_spec.rb +37 -0
  26. data/spec/api/groups_spec.rb +22 -0
  27. data/spec/api/jobs_spec.rb +31 -0
  28. data/spec/api/profiles_spec.rb +109 -0
  29. data/spec/faraday_middleware/linkedin_error_response_spec.rb +47 -0
  30. data/spec/fixtures/requests/access_token.yml +37 -0
  31. data/spec/fixtures/requests/company-search/by_keywords_options_with_fields.yml +232 -0
  32. data/spec/fixtures/requests/company-search/by_keywords_string_parameter.yml +80 -0
  33. data/spec/fixtures/requests/company-search/by_single_keywords_option.yml +80 -0
  34. data/spec/fixtures/requests/company-search/by_single_keywords_option_with_facets_to_return.yml +80 -0
  35. data/spec/fixtures/requests/company-search/by_single_keywords_option_with_pagination.yml +74 -0
  36. data/spec/fixtures/requests/company.yml +81 -0
  37. data/spec/fixtures/requests/invalid.yml +53 -0
  38. data/spec/fixtures/requests/people-search/by_company_name_option.yml +93 -0
  39. data/spec/fixtures/requests/people-search/by_first_name_and_last_name_options.yml +100 -0
  40. data/spec/fixtures/requests/people-search/by_first_name_and_last_name_options_with_fields.yml +112 -0
  41. data/spec/fixtures/requests/people-search/by_keywords_string_parameter.yml +53 -0
  42. data/spec/fixtures/requests/people-search/by_single_keywords_option.yml +53 -0
  43. data/spec/fixtures/requests/people-search/by_single_keywords_option_with_pagination.yml +45 -0
  44. data/spec/fixtures/requests/request_token/request_token.yml +37 -0
  45. data/spec/fixtures/requests/request_token/with_callback.yml +37 -0
  46. data/spec/spec_helper.rb +17 -0
  47. metadata +132 -3
@@ -0,0 +1,20 @@
1
+ module LinkedIn
2
+ ADDRESS_FIELDS = [ 'street1', 'street2', 'city', 'state', 'postal-code', 'country-code', 'region-code' ]
3
+ CONTACT_INFO_FIELDS = [ 'phone1', 'phone2', 'fax' ]
4
+ COMPANY_LOCATIONS_FIELDS = ['description', 'is-headquarters', 'is-active', { 'address' => ADDRESS_FIELDS }, { 'contact-info' => CONTACT_INFO_FIELDS } ]
5
+ COMPANY_BASE_FIELDS = [ 'id', 'name', 'universal-name', 'email-domains', 'company-type', 'ticker', 'website-url',
6
+ 'industries', 'status', 'logo-url', 'square-logo-url', 'blog-rss-url', 'twitter-id', 'employee-count-range',
7
+ 'specialties', { 'locations' => COMPANY_LOCATIONS_FIELDS }, 'description', 'stock-exchange', 'founded-year',
8
+ 'end-year', 'num-followers' ]
9
+
10
+ class Company < Base
11
+ def self.find(id, *fields)
12
+ find_by( { id: id }, *fields)
13
+ end
14
+
15
+ def self.find_by(selector, *fields)
16
+ fields = fields.blank? ? COMPANY_BASE_FIELDS : fields
17
+ Company.new client.company( selector: selector, fields: fields )
18
+ end
19
+ end
20
+ end
@@ -1,55 +1,41 @@
1
1
  module LinkedIn
2
2
  module Configuration
3
3
  module ClassConfiguration
4
- BASE_OPTIONS = {
5
- authorize_path: '/uas/oauth2/authorization',
6
- access_token_path: '/uas/oauth2/accessToken',
7
- api_host: 'https://api.linkedin.com',
8
- auth_host: 'https://www.linkedin.com',
9
-
10
- key: nil,
11
- secret: nil,
12
-
13
- scope: 'r_basicprofile',
14
- state: Utils.generate_random_state,
15
- redirect_uri: 'http://localhost'
16
- }
17
-
18
- def options
19
- @options ||= reset
4
+ def config
5
+ @config ||= reset
20
6
  end
21
7
 
22
8
  def reset
23
- @options = OpenStruct.new BASE_OPTIONS
9
+ @config = OpenStruct.new default_config
24
10
  end
25
11
  end
26
12
 
27
13
  module InstanceConfiguration
28
- def options
29
- @options ||= reset
14
+ def config
15
+ @config ||= reset
30
16
  end
31
17
 
32
18
  def reset
33
- @options = self.class.options.clone
19
+ @config = self.class.config.dup
34
20
  end
35
21
  end
36
22
 
37
23
  module BaseConfiguration
38
- def configure(options={}, &block)
39
- self.options.marshal_load self.options.to_h.merge(options)
24
+ def configure(config={}, &block)
25
+ self.config.marshal_load self.config.to_h.merge(config)
40
26
 
41
- yield self if block_given?
27
+ yield self.config if block_given?
42
28
 
43
- self.options
29
+ self.config
44
30
  end
45
31
 
46
- def method_missing(method, *args, &block)
47
- return self.options.send(method, *args, &block) if self.options.respond_to? method
48
- super
32
+ def load(file_path='linkedin.yml')
33
+ config = YAML::load(File.open(file_path)).symbolize_keys
34
+ configure config
49
35
  end
50
36
 
51
37
  def defaults(*keys)
52
- options.to_h.slice keys
38
+ config.to_h.slice *keys
53
39
  end
54
40
  end
55
41
 
@@ -1,5 +1,25 @@
1
1
  module LinkedIn
2
- class Error < StandardError; end
2
+ class Error < StandardError
3
+ extend Forwardable
4
+
5
+ attr_accessor :request, :response
6
+ delegate [:status, :body] => :response
7
+
8
+ def initialize(error_data={})
9
+ if hash = Hash.try_convert(error_data)
10
+ self.request, self.response = hash[:request], hash[:response]
11
+ end
12
+
13
+ super (self.response && self.response.body['message']) || error_data
14
+ end
15
+ end
16
+
17
+ class BadRequest < Error; end
18
+ class Unauthorized < Error; end
19
+ class Forbidden < Error; end
20
+ class NotFound < Error; end
21
+ class InternalServerError < Error; end
22
+
3
23
  class CSRF < Error
4
24
  def initialize(expected=nil, received=nil)
5
25
  additional = "Excepted '#{expected}' but received '#{received}'" if expected
@@ -0,0 +1,10 @@
1
+ module LinkedIn
2
+ module FaradayMiddleware
3
+ require 'linkedin/faraday_middleware/linkedin_format_request'
4
+ require 'linkedin/faraday_middleware/linkedin_error_response'
5
+
6
+ Faraday.register_middleware :request, linkedin_format: lambda { LinkedinFormatRequest }
7
+
8
+ Faraday.register_middleware :response, linkedin_errors: lambda { LinkedinErrorResponse }
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ module LinkedIn
2
+ module FaradayMiddleware
3
+ class LinkedinErrorResponse < Faraday::Response::Middleware
4
+ ERRORS = {
5
+ 400 => BadRequest,
6
+ 401 => Unauthorized,
7
+ 403 => Forbidden,
8
+ 404 => NotFound,
9
+ 500 => InternalServerError
10
+ }
11
+
12
+ def on_complete(env)
13
+ status = env[:status].to_i
14
+ error = ERRORS[status] || Error
15
+
16
+ raise error.new response_values(env) if status >= 400
17
+ end
18
+
19
+ def response_values(env)
20
+ {
21
+ request: OpenStruct.new(headers: env[:request_headers], uri: env[:url]),
22
+ response: env[:response]
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,27 @@
1
+ module LinkedIn
2
+ module FaradayMiddleware
3
+ class LinkedinFormatRequest < Faraday::Middleware
4
+ def initialize(app=nil, options={})
5
+ super app
6
+
7
+ @options = options
8
+ @request_format = options[:request_format]
9
+ end
10
+
11
+ def call(env)
12
+ set_request_format! env[:url] unless has_format?(env[:url])
13
+
14
+ @app.call env
15
+ end
16
+
17
+ def has_format?(url)
18
+ !(url.query =~ /(^|&)format=/).nil?
19
+ end
20
+
21
+ def set_request_format!(url)
22
+ ar = URI.decode_www_form(url.query || []) << ['format', @request_format]
23
+ url.query = URI.encode_www_form(ar)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module LinkedIn
2
+ module Industry
3
+ def self.all
4
+ LinkedIn::API::Industries::INDUSTRIES
5
+ end
6
+
7
+ def self.[](code)
8
+ find_by_code(code)
9
+ end
10
+
11
+ def self.find_by_code(code)
12
+ all.detect { |indust| indust[:code] == code }
13
+ end
14
+
15
+ def self.find_by_group(*groups)
16
+ all.reject { |indust| (groups & indust[:group]) != groups }
17
+ end
18
+
19
+ def self.find_by_description(description)
20
+ all.detect { |indust| indust[:description].to_s.downcase == description.to_s.downcase }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module LinkedIn
2
+ class Profile < Base
3
+ def connections
4
+ @connections ||= client.connections(selector: { id: self.id })['values'].map { |c| Profile.new c }
5
+ end
6
+
7
+ def self.current(*fields)
8
+ find_by( { }, *fields)
9
+ end
10
+
11
+ def self.find(id, *fields)
12
+ find_by( { id: id }, *fields)
13
+ end
14
+
15
+ def self.find_by(selector, *fields)
16
+ fields = fields.blank? ? LinkedIn.r_basicprofile : fields
17
+ Profile.new client.profile( selector: selector, fields: fields )
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module LinkedIn
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/linkedin2.rb CHANGED
@@ -1,10 +1,59 @@
1
+ require 'forwardable'
1
2
  require 'oauth2'
2
- require 'byebug'
3
- require 'active_support/all'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+ require 'active_support/time'
6
+ require 'faraday'
7
+ require 'faraday_middleware'
8
+ require 'yaml'
9
+ require 'hashie'
4
10
 
5
11
  require 'linkedin/version'
6
12
  require 'linkedin/error'
7
13
  require 'linkedin/utils'
8
- require 'linkedin/api/authentication'
9
14
  require 'linkedin/configuration'
15
+ require 'linkedin/base'
16
+ require 'linkedin/profile'
17
+ require 'linkedin/company'
18
+ require 'linkedin/industry'
19
+ require 'linkedin/faraday_middleware'
20
+ require 'linkedin/api'
10
21
  require 'linkedin/client'
22
+
23
+ module LinkedIn
24
+ def new(options={}, &block)
25
+ Client.new options, &block
26
+ end
27
+
28
+ def self.method_missing(method, *args, &block)
29
+ Client.send(method, *args, &block) if Client.respond_to?(method)
30
+ end
31
+
32
+ def self.r_basicprofile
33
+ @@r_basicprofile ||= API::Permissions::R_BASICPROFILE
34
+ end
35
+
36
+ def self.r_emailaddress
37
+ @@r_emailaddress ||= API::Permissions::R_EMAIL
38
+ end
39
+
40
+ def self.r_fullprofile
41
+ @@r_fullprofile ||= API::Permissions::R_FULLPROFILE
42
+ end
43
+
44
+ def self.r_contactinfo
45
+ @@r_contactinfo ||= API::Permissions::R_CONTACTINFO
46
+ end
47
+
48
+ def self.r_network
49
+ @@r_network ||= API::Permissions::R_NETWORK
50
+ end
51
+
52
+ def self.rw_groups
53
+ @@rw_groups ||= API::Permissions::RW_GROUPS
54
+ end
55
+
56
+ def self.rw_nus
57
+ @@rw_nus ||= API::Permissions::RW_NUS
58
+ end
59
+ end
data/linkedin.gemspec CHANGED
@@ -19,9 +19,14 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'oauth2', '~> 0.9'
22
+ spec.add_dependency 'hashie', '~> 2.0.5'
22
23
  spec.add_dependency 'activesupport', '~> 4.0'
24
+ spec.add_dependency 'faraday_middleware', '~> 0.9'
23
25
 
24
26
  spec.add_development_dependency 'bundler', '~> 1.3'
25
27
  spec.add_development_dependency 'rake'
26
28
  spec.add_development_dependency 'byebug'
29
+ spec.add_development_dependency 'pry'
30
+ spec.add_development_dependency 'rspec', '~> 2.14'
31
+ spec.add_development_dependency 'vcr', '~> 2.5'
27
32
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe LinkedIn::API::Companies do
4
+ subject { LinkedIn::Client }
5
+
6
+ describe '#company' do
7
+ it "should be able to view a company profile" do
8
+ pending "https://api.linkedin.com/v1/companies/id=1586"
9
+ subject.company(:id => 1586)
10
+ end
11
+
12
+ it "should be able to view a company by universal name" do
13
+ pending "https://api.linkedin.com/v1/companies/universal-name=acme"
14
+ subject.company(:name => 'acme')
15
+ end
16
+
17
+ it "should be able to view a company by e-mail domain" do
18
+ pending "https://api.linkedin.com/v1/companies?email-domain=acme.com"
19
+ subject.company(:domain => 'acme.com')
20
+ end
21
+
22
+ it "should load correct company data" do
23
+ pending
24
+ data = subject.company(:id => 1586, :fields => %w{ id name industry locations:(address:(city state country-code) is-headquarters) employee-count-range })
25
+ data.id.should == 1586
26
+ data.name.should == "Amazon"
27
+ data.employee_count_range.name.should == "10001+"
28
+ data.industry.should == "Internet"
29
+ data.locations.all[0].address.city.should == "Seattle"
30
+ data.locations.all[0].is_headquarters.should == true
31
+ end
32
+
33
+ it "should raise AccessDeniedError when LinkedIn returns 403 status code" do
34
+ pending
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe LinkedIn::API::Groups do
4
+ describe '#jobs' do
5
+ it "should be able to list group memberships for a profile" do
6
+ pending "https://api.linkedin.com/v1/people/~/group-memberships"
7
+ subject.group_memberships
8
+ end
9
+
10
+ it "should be able to join a group" do
11
+ pending "https://api.linkedin.com/v1/people/~/group-memberships/123"
12
+
13
+ response = subject.join_group(123)
14
+ response.body.should == nil
15
+ response.code.should == "201"
16
+ end
17
+
18
+ it "should raise AccessDeniedError when LinkedIn returns 403 status code" do
19
+ pending
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe LinkedIn::API::Jobs do
4
+ describe '#jobs' do
5
+ it "should be able to view a job listing" do
6
+ pending "https://api.linkedin.com/v1/jobs/id=1586"
7
+ subject.job(:id => 1586)
8
+ end
9
+
10
+ it "should be able to view its job bookmarks" do
11
+ pending "https://api.linkedin.com/v1/people/~/job-bookmarks"
12
+ subject.job_bookmarks
13
+ end
14
+
15
+ it "should be able to view its job suggestion" do
16
+ pending "https://api.linkedin.com/v1/people/~/suggestions/job-suggestions"
17
+ subject.job_suggestions
18
+ end
19
+
20
+ it "should be able to add a bookmark" do
21
+ pending "https://api.linkedin.com/v1/people/~/job-bookmarks"
22
+ response = subject.add_job_bookmark(:id => 1452577)
23
+ response.body.should == nil
24
+ response.code.should == "201"
25
+ end
26
+
27
+ it "should raise AccessDeniedError when LinkedIn returns 403 status code" do
28
+ pending
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe LinkedIn::API::Profiles do
4
+ subject { LinkedIn::Client }
5
+
6
+ describe '#profile' do
7
+ it "should be able to view the account profile" do
8
+ pending "https://api.linkedin.com/v1/people/~"
9
+ subject.profile
10
+ end
11
+
12
+ it "should be able to view public profiles" do
13
+ pending "https://api.linkedin.com/v1/people/id=123"
14
+ subject.profile(:id => 123)
15
+ end
16
+
17
+ it "should be able to view connections" do
18
+ pending "https://api.linkedin.com/v1/people/~/connections"
19
+ subject.connections
20
+ end
21
+
22
+ it "should be able to view network_updates" do
23
+ pending "https://api.linkedin.com/v1/people/~/network/updates"
24
+ subject.network_updates
25
+ end
26
+
27
+ it "should be able to view network_update's comments" do
28
+ pending "https://api.linkedin.com/v1/people/~/network/updates/key=network_update_key/update-comments"
29
+ subject.share_comments("network_update_key")
30
+ end
31
+
32
+ it "should be able to view network_update's likes" do
33
+ pending "https://api.linkedin.com/v1/people/~/network/updates/key=network_update_key/likes"
34
+ subject.share_likes("network_update_key")
35
+ end
36
+
37
+ it "should be able to search with a keyword if given a String" do
38
+ pending "https://api.linkedin.com/v1/people-search?keywords=business"
39
+ subject.search("business")
40
+ end
41
+
42
+ it "should be able to search with an option" do
43
+ pending "https://api.linkedin.com/v1/people-search?first-name=Javan"
44
+ subject.search(:first_name => "Javan")
45
+ end
46
+
47
+ it "should be able to search with an option and fetch specific fields" do
48
+ pending "https://api.linkedin.com/v1/people-search:(num-results,total)?first-name=Javan"
49
+ subject.search(:first_name => "Javan", :fields => ["num_results", "total"])
50
+ end
51
+
52
+ it "should be able to share a new status" do
53
+ pending "https://api.linkedin.com/v1/people/~/shares"
54
+ response = subject.add_share(:comment => "Testing, 1, 2, 3")
55
+ response.body.should == nil
56
+ response.code.should == "201"
57
+ end
58
+
59
+ it "returns the shares for a person" do
60
+ pending "https://api.linkedin.com/v1/people/~/network/updates?type=SHAR&scope=self&after=1234&count=35"
61
+ subject.shares(:after => 1234, :count => 35)
62
+ end
63
+
64
+ it "should be able to comment on network update" do
65
+ pending "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/update-comments"
66
+ response = subject.update_comment('SOMEKEY', "Testing, 1, 2, 3")
67
+ response.body.should == nil
68
+ response.code.should == "201"
69
+ end
70
+
71
+ it "should be able to send a message" do
72
+ pending "https://api.linkedin.com/v1/people/~/mailbox"
73
+ response = subject.send_message("subject", "body", ["recip1", "recip2"])
74
+ response.body.should == nil
75
+ response.code.should == "201"
76
+ end
77
+
78
+ it "should be able to like a network update" do
79
+ pending "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/is-liked"
80
+ response = subject.like_share('SOMEKEY')
81
+ response.body.should == nil
82
+ response.code.should == "201"
83
+ end
84
+
85
+ it "should be able to unlike a network update" do
86
+ pending "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/is-liked"
87
+ response = subject.unlike_share('SOMEKEY')
88
+ response.body.should == nil
89
+ response.code.should == "201"
90
+ end
91
+
92
+ it "should be able to pass down the additional arguments to OAuth's get_request_token" do
93
+ pending 'this test is bullshit, reasses it in the context of this gem'
94
+ consumer.should_receive(:get_request_token).with(
95
+ {:oauth_callback => "http://localhost:3000/auth/callback"}, :scope => "rw_nus").and_return("request_token")
96
+
97
+ request_token = subject.request_token(
98
+ {:oauth_callback => "http://localhost:3000/auth/callback"}, :scope => "rw_nus"
99
+ )
100
+
101
+ request_token.should == "request_token"
102
+ end
103
+
104
+ it "should raise AccessDeniedError when LinkedIn returns 403 status code" do
105
+ pending "https://api.linkedin.com/v1/people-search?first-name=Javan"
106
+ expect{ subject.search(:first_name => "Javan") }.to raise_error(LinkedIn::Forbidden)
107
+ end
108
+ end
109
+ end