linkedin2 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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