linkedin-build 1.1.14

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +14 -0
  3. data/.gemtest +0 -0
  4. data/.gitignore +49 -0
  5. data/.rspec +1 -0
  6. data/.travis.yml +6 -0
  7. data/.yardopts +7 -0
  8. data/CHANGELOG.md +99 -0
  9. data/EXAMPLES.md +202 -0
  10. data/Gemfile +11 -0
  11. data/LICENSE +22 -0
  12. data/README.md +43 -0
  13. data/Rakefile +15 -0
  14. data/lib/linked_in/api.rb +38 -0
  15. data/lib/linked_in/api/communications.rb +44 -0
  16. data/lib/linked_in/api/companies.rb +129 -0
  17. data/lib/linked_in/api/groups.rb +115 -0
  18. data/lib/linked_in/api/jobs.rb +64 -0
  19. data/lib/linked_in/api/people.rb +73 -0
  20. data/lib/linked_in/api/query_helpers.rb +86 -0
  21. data/lib/linked_in/api/share_and_social_stream.rb +137 -0
  22. data/lib/linked_in/client.rb +51 -0
  23. data/lib/linked_in/errors.rb +29 -0
  24. data/lib/linked_in/helpers.rb +6 -0
  25. data/lib/linked_in/helpers/authorization.rb +69 -0
  26. data/lib/linked_in/helpers/request.rb +85 -0
  27. data/lib/linked_in/mash.rb +95 -0
  28. data/lib/linked_in/search.rb +71 -0
  29. data/lib/linked_in/version.rb +11 -0
  30. data/lib/linkedin.rb +35 -0
  31. data/linkedin-build.gemspec +28 -0
  32. data/spec/cases/api_spec.rb +308 -0
  33. data/spec/cases/linkedin_spec.rb +37 -0
  34. data/spec/cases/mash_spec.rb +113 -0
  35. data/spec/cases/oauth_spec.rb +178 -0
  36. data/spec/cases/search_spec.rb +234 -0
  37. data/spec/fixtures/cassette_library/LinkedIn_Api/Company_API.yml +81 -0
  38. data/spec/fixtures/cassette_library/LinkedIn_Api/Company_API/should_load_correct_company_data.yml +81 -0
  39. data/spec/fixtures/cassette_library/LinkedIn_Client/_authorize_from_request/should_return_a_valid_access_token.yml +37 -0
  40. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/with_a_callback_url/should_return_a_valid_access_token.yml +37 -0
  41. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/with_default_options/should_return_a_valid_request_token.yml +37 -0
  42. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_company_name_option/should_perform_a_search.yml +92 -0
  43. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_email_address/should_perform_a_people_search.yml +57 -0
  44. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options/should_perform_a_search.yml +100 -0
  45. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options_with_fields/should_perform_a_search.yml +114 -0
  46. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_keywords_string_parameter/should_perform_a_search.yml +52 -0
  47. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_multiple_email_address/should_perform_a_multi-email_search.yml +59 -0
  48. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_single_keywords_option/should_perform_a_search.yml +52 -0
  49. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_single_keywords_option_with_pagination/should_perform_a_search.yml +43 -0
  50. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/email_search_returns_unauthorized/should_raise_an_unauthorized_error.yml +59 -0
  51. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_options_with_fields/should_perform_a_search.yml +43 -0
  52. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_string_parameter/should_perform_a_company_search.yml +80 -0
  53. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option/should_perform_a_company_search.yml +80 -0
  54. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_facets_to_return/should_return_a_facet.yml +80 -0
  55. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_pagination/should_perform_a_search.yml +74 -0
  56. data/spec/helper.rb +34 -0
  57. metadata +282 -0
@@ -0,0 +1,95 @@
1
+ require 'hashie'
2
+ require 'multi_json'
3
+
4
+ module LinkedIn
5
+
6
+ # The generalized pseudo-object that is returned for all query
7
+ # requests.
8
+ class Mash < ::Hashie::Mash
9
+
10
+ # Convert a json string to a Mash
11
+ #
12
+ # @param [String] json_string
13
+ # @return [LinkedIn::Mash]
14
+ def self.from_json(json_string)
15
+ result_hash = ::MultiJson.decode(json_string)
16
+ new(result_hash)
17
+ end
18
+
19
+ # Returns a Date if we have year, month and day, and no conflicting key
20
+ #
21
+ # @return [Date]
22
+ def to_date
23
+ if !self.has_key?('to_date') && contains_date_fields?
24
+ Date.civil(self.year, self.month, self.day)
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ # Returns the id of the object from LinkedIn
31
+ #
32
+ # @return [String]
33
+ def id
34
+ if self['id']
35
+ self['id']
36
+ else
37
+ self['_key']
38
+ end
39
+ end
40
+
41
+ # Convert the 'timestamp' field from a string to a Time object
42
+ #
43
+ # @return [Time]
44
+ def timestamp
45
+ value = self['timestamp']
46
+ if value.kind_of? Integer
47
+ value = value / 1000 if value > 9999999999
48
+ Time.at(value)
49
+ else
50
+ value
51
+ end
52
+ end
53
+
54
+ # Return the results array from the query
55
+ #
56
+ # @return [Array]
57
+ def all
58
+ super || []
59
+ end
60
+
61
+ protected
62
+
63
+ def contains_date_fields?
64
+ self.year? && self.month? && self.day?
65
+ end
66
+
67
+ # overload the convert_key mash method so that the LinkedIn
68
+ # keys are made a little more ruby-ish
69
+ def convert_key(key)
70
+ case key.to_s
71
+ when '_total'
72
+ 'total'
73
+ when 'values'
74
+ 'all'
75
+ when 'numResults'
76
+ 'total_results'
77
+ else
78
+ underscore(key)
79
+ end
80
+ end
81
+
82
+ # borrowed from ActiveSupport
83
+ # no need require an entire lib when we only need one method
84
+ def underscore(camel_cased_word)
85
+ word = camel_cased_word.to_s.dup
86
+ word.gsub!(/::/, '/')
87
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
88
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
89
+ word.tr!("-", "_")
90
+ word.downcase!
91
+ word
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,71 @@
1
+ module LinkedIn
2
+
3
+ module Search
4
+
5
+ # Retrieve search results of the given object type
6
+ #
7
+ # Permissions: (for people search only) r_network
8
+ #
9
+ # @note People Search API is a part of the Vetted API Access Program. You
10
+ # must apply and get approval before using this API
11
+ #
12
+ # @see http://developer.linkedin.com/documents/people-search-api People Search
13
+ # @see http://developer.linkedin.com/documents/job-search-api Job Search
14
+ # @see http://developer.linkedin.com/documents/company-search Company Search
15
+ #
16
+ # @param [Hash] options search input fields
17
+ # @param [String] type type of object to return ('people', 'job' or 'company')
18
+ # @return [LinkedIn::Mash]
19
+ def search(options={}, type='people')
20
+
21
+ path = "/#{type.to_s}-search"
22
+
23
+ if options.is_a?(Hash)
24
+ fields = options.delete(:fields)
25
+ path += field_selector(fields) if fields
26
+ end
27
+
28
+ options = { :keywords => options } if options.is_a?(String)
29
+ options = format_options_for_query(options)
30
+
31
+ result_json = get(to_uri(path, options))
32
+
33
+ Mash.from_json(result_json)
34
+ end
35
+
36
+ private
37
+
38
+ def format_options_for_query(opts)
39
+ opts.inject({}) do |list, kv|
40
+ key, value = kv.first.to_s.gsub("_","-"), kv.last
41
+ list[key] = sanitize_value(value)
42
+ list
43
+ end
44
+ end
45
+
46
+ def sanitize_value(value)
47
+ value = value.join("+") if value.is_a?(Array)
48
+ value = value.gsub(" ", "+") if value.is_a?(String)
49
+ value
50
+ end
51
+
52
+ def field_selector(fields)
53
+ result = ":("
54
+ fields = fields.to_a.map do |field|
55
+ if field.is_a?(Hash)
56
+ innerFields = []
57
+ field.each do |key, value|
58
+ innerFields << key.to_s.gsub("_","-") + field_selector(value)
59
+ end
60
+ innerFields.join(',')
61
+ else
62
+ field.to_s.gsub("_","-")
63
+ end
64
+ end
65
+ result += fields.join(',')
66
+ result += ")"
67
+ result
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,11 @@
1
+ module LinkedIn
2
+
3
+ module VERSION #:nodoc:
4
+ MAJOR = 1
5
+ MINOR = 1
6
+ PATCH = 14
7
+ PRE = nil
8
+ STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
9
+ end
10
+
11
+ end
@@ -0,0 +1,35 @@
1
+ require 'oauth'
2
+
3
+ module LinkedIn
4
+
5
+ class << self
6
+ attr_accessor :token, :secret, :default_profile_fields
7
+
8
+ # config/initializers/linkedin.rb (for instance)
9
+ #
10
+ # ```ruby
11
+ # LinkedIn.configure do |config|
12
+ # config.token = 'consumer_token'
13
+ # config.secret = 'consumer_secret'
14
+ # config.default_profile_fields = ['educations', 'positions']
15
+ # end
16
+ # ```
17
+ # elsewhere
18
+ #
19
+ # ```ruby
20
+ # client = LinkedIn::Client.new
21
+ # ```
22
+ def configure
23
+ yield self
24
+ true
25
+ end
26
+ end
27
+
28
+ autoload :Api, "linked_in/api"
29
+ autoload :Client, "linked_in/client"
30
+ autoload :Mash, "linked_in/mash"
31
+ autoload :Errors, "linked_in/errors"
32
+ autoload :Helpers, "linked_in/helpers"
33
+ autoload :Search, "linked_in/search"
34
+ autoload :Version, "linked_in/version"
35
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../lib/linked_in/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.add_dependency 'hashie', '3.5.6' #'~> 3.0'
6
+ gem.add_dependency 'multi_json', '~> 1.0'
7
+ gem.add_dependency 'oauth', '0.5.1' # '~> 0.4'
8
+ gem.add_dependency 'oauth2', '1.4.0'
9
+ # gem.add_development_dependency 'json', '~> 1.6'
10
+ gem.add_development_dependency 'rake', '~> 10'
11
+ gem.add_development_dependency 'yard'
12
+ gem.add_development_dependency 'kramdown'
13
+ gem.add_development_dependency 'rspec', '~> 2.13'
14
+ gem.add_development_dependency 'simplecov', '~> 0.7'
15
+ gem.add_development_dependency 'vcr', '~> 2.5'
16
+ gem.add_development_dependency 'webmock', '~> 1.11'
17
+ gem.authors = ['Matthew Kirk', 'Wynn Netherland', 'Josh Kalderimis']
18
+ gem.description = 'Ruby wrapper for the LinkedIn API'
19
+ gem.email = ['meteor.kirk@gmail.com', 'wynn.netherland@gmail.com', 'josh.kalderimis@gmail.com']
20
+ gem.files = `git ls-files`.split("\n")
21
+ gem.homepage = 'http://github.com/hexgnu/linkedin'
22
+ gem.name = 'linkedin-build'
23
+ gem.licenses = %w[MIT]
24
+ gem.require_paths = ['lib']
25
+ gem.summary = 'This gem interfaces with the Linkedin XML and JSON APis'
26
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
+ gem.version = LinkedIn::VERSION::STRING
28
+ end
@@ -0,0 +1,308 @@
1
+ require 'helper'
2
+
3
+ describe LinkedIn::Api do
4
+ before do
5
+ LinkedIn.default_profile_fields = nil
6
+ client.stub(:consumer).and_return(consumer)
7
+ client.authorize_from_access('atoken', 'asecret')
8
+ end
9
+
10
+ let(:client){LinkedIn::Client.new('token', 'secret')}
11
+ let(:consumer){OAuth::Consumer.new('token', 'secret', {:site => 'https://api.linkedin.com'})}
12
+
13
+ it "should be able to view the account profile" do
14
+ stub_request(:get, "https://api.linkedin.com/v1/people/~").to_return(:body => "{}")
15
+ client.profile.should be_an_instance_of(LinkedIn::Mash)
16
+ end
17
+
18
+ it "should be able to view public profiles" do
19
+ stub_request(:get, "https://api.linkedin.com/v1/people/id=123").to_return(:body => "{}")
20
+ client.profile(:id => 123).should be_an_instance_of(LinkedIn::Mash)
21
+ end
22
+
23
+ it "should be able to view the picture urls" do
24
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/picture-urls::(original)").to_return(:body => "{}")
25
+ client.picture_urls.should be_an_instance_of(LinkedIn::Mash)
26
+ end
27
+
28
+ it "should be able to view connections" do
29
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/connections").to_return(:body => "{}")
30
+ client.connections.should be_an_instance_of(LinkedIn::Mash)
31
+ end
32
+
33
+ it "should be able to view new connections" do
34
+ modified_since = Time.now.to_i * 1000
35
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/connections?modified=new&modified-since=#{modified_since}").to_return(:body => "{}")
36
+ client.new_connections(modified_since).should be_an_instance_of(LinkedIn::Mash)
37
+ end
38
+
39
+ it "should be able to view network_updates" do
40
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates").to_return(:body => "{}")
41
+ client.network_updates.should be_an_instance_of(LinkedIn::Mash)
42
+ end
43
+
44
+ it "should be able to view network_update's comments" do
45
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates/key=network_update_key/update-comments").to_return(:body => "{}")
46
+ client.share_comments("network_update_key").should be_an_instance_of(LinkedIn::Mash)
47
+ end
48
+
49
+ it "should be able to view network_update's likes" do
50
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates/key=network_update_key/likes").to_return(:body => "{}")
51
+ client.share_likes("network_update_key").should be_an_instance_of(LinkedIn::Mash)
52
+ end
53
+
54
+ it "should be able to search with a keyword if given a String" do
55
+ stub_request(:get, "https://api.linkedin.com/v1/people-search?keywords=business").to_return(:body => "{}")
56
+ client.search("business").should be_an_instance_of(LinkedIn::Mash)
57
+ end
58
+
59
+ it "should be able to search with an option" do
60
+ stub_request(:get, "https://api.linkedin.com/v1/people-search?first-name=Javan").to_return(:body => "{}")
61
+ client.search(:first_name => "Javan").should be_an_instance_of(LinkedIn::Mash)
62
+ end
63
+
64
+ it "should be able to search with an option and fetch specific fields" do
65
+ stub_request(:get, "https://api.linkedin.com/v1/people-search:(num-results,total)?first-name=Javan").to_return(
66
+ :body => "{}")
67
+ client.search(:first_name => "Javan", :fields => ["num_results", "total"]).should be_an_instance_of(LinkedIn::Mash)
68
+ end
69
+
70
+ it "should be able to share a new status" do
71
+ stub_request(:post, "https://api.linkedin.com/v1/people/~/shares").to_return(:body => "", :status => 201)
72
+ response = client.add_share(:comment => "Testing, 1, 2, 3")
73
+ response.body.should == nil
74
+ response.status.should == "201"
75
+ end
76
+
77
+ it "should be able to share a new company status" do
78
+ stub_request(:post, "https://api.linkedin.com/v1/companies/123456/shares").to_return(:body => "", :status => 201)
79
+ response = client.add_company_share("123456", { :comment => "Testing, 1, 2, 3" })
80
+ response.body.should == nil
81
+ response.status.should == "201"
82
+ end
83
+
84
+ it "returns the shares for a person" do
85
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates?type=SHAR&scope=self&after=1234&count=35").to_return(
86
+ :body => "{}")
87
+ client.shares(:after => 1234, :count => 35)
88
+ end
89
+
90
+ it "should be able to fetch a single share" do
91
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates/key=network_update_key").to_return(:body => "{}")
92
+ client.share("network_update_key").should be_an_instance_of(LinkedIn::Mash)
93
+ end
94
+
95
+ it "should be able to comment on network update" do
96
+ stub_request(:post, "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/update-comments").to_return(
97
+ :body => "", :status => 201)
98
+ response = client.update_comment('SOMEKEY', "Testing, 1, 2, 3")
99
+ response.body.should == nil
100
+ response.status.should == "201"
101
+ end
102
+
103
+ it "should be able to like a network update" do
104
+ stub_request(:put, "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/is-liked").
105
+ with(:body => "true").to_return(:body => "", :status => 201)
106
+ response = client.like_share('SOMEKEY')
107
+ response.body.should == nil
108
+ response.status.should == "201"
109
+ end
110
+
111
+ it "should be able to unlike a network update" do
112
+ stub_request(:put, "https://api.linkedin.com/v1/people/~/network/updates/key=SOMEKEY/is-liked").
113
+ with(:body => "false").to_return(:body => "", :status => 201)
114
+ response = client.unlike_share('SOMEKEY')
115
+ response.body.should == nil
116
+ response.status.should == "201"
117
+ end
118
+
119
+ it "should be able to pass down the additional arguments to OAuth's get_request_token" do
120
+ consumer.should_receive(:get_request_token).with(
121
+ {:oauth_callback => "http://localhost:3000/auth/callback"}, :scope => "rw_nus").and_return("request_token")
122
+
123
+ request_token = client.request_token(
124
+ {:oauth_callback => "http://localhost:3000/auth/callback"}, :scope => "rw_nus"
125
+ )
126
+
127
+ request_token.should == "request_token"
128
+ end
129
+
130
+ context "Company API", :vcr do
131
+
132
+ it "should be able to view a company profile" do
133
+ stub_request(:get, "https://api.linkedin.com/v1/companies/id=1586").to_return(:body => "{}")
134
+ client.company(:id => 1586).should be_an_instance_of(LinkedIn::Mash)
135
+ end
136
+
137
+ it "should be able to view a company by universal name" do
138
+ stub_request(:get, "https://api.linkedin.com/v1/companies/universal-name=acme").to_return(:body => "{}")
139
+ client.company(:name => 'acme').should be_an_instance_of(LinkedIn::Mash)
140
+ end
141
+
142
+ it "should be able to view a company by e-mail domain" do
143
+ stub_request(:get, "https://api.linkedin.com/v1/companies?email-domain=acme.com").to_return(:body => "{}")
144
+ client.company(:domain => 'acme.com').should be_an_instance_of(LinkedIn::Mash)
145
+ end
146
+
147
+ it "should be able to view a user's company pages" do
148
+ stub_request(:get, "https://api.linkedin.com/v1/companies?is-company-admin=true").to_return(:body => "{}")
149
+ client.company(:is_admin => 'true').should be_an_instance_of(LinkedIn::Mash)
150
+ end
151
+
152
+ it "should be able to page a user's company pages" do
153
+ stub_request(:get, "https://api.linkedin.com/v1/companies?is-company-admin=true&count=10&start=0").to_return(:body => "{}")
154
+ client.company(:is_admin => 'true', :count => 10, :start => 0).should be_an_instance_of(LinkedIn::Mash)
155
+ end
156
+
157
+ it "should load correct company data" do
158
+ client.company(:id => 1586).name.should == "Amazon"
159
+
160
+ data = client.company(:id => 1586, :fields => %w{ id name industry locations:(address:(city state country-code) is-headquarters) employee-count-range })
161
+ data.id.should == 1586
162
+ data.name.should == "Amazon"
163
+ data.employee_count_range.name.should == "10001+"
164
+ data.industry.should == "Internet"
165
+ data.locations.all[0].address.city.should == "Seattle"
166
+ data.locations.all[0].is_headquarters.should == true
167
+ end
168
+
169
+ it "should be able to view company_updates" do
170
+ stub_request(:get, "https://api.linkedin.com/v1/companies/id=1586/updates").to_return(:body => "{}")
171
+ client.company_updates(:id => 1586).should be_an_instance_of(LinkedIn::Mash)
172
+ end
173
+
174
+ it "should be able to view company_statistic" do
175
+ stub_request(:get, "https://api.linkedin.com/v1/companies/id=1586/company-statistics").to_return(:body => "{}")
176
+ client.company_statistics(:id => 1586).should be_an_instance_of(LinkedIn::Mash)
177
+ end
178
+
179
+ it "should be able to view company updates comments" do
180
+ stub_request(:get, "https://api.linkedin.com/v1/companies/id=1586/updates/key=company_update_key/update-comments").to_return(:body => "{}")
181
+ client.company_updates_comments("company_update_key", :id => 1586).should be_an_instance_of(LinkedIn::Mash)
182
+ end
183
+
184
+ it "should be able to view company updates likes" do
185
+ stub_request(:get, "https://api.linkedin.com/v1/companies/id=1586/updates/key=company_update_key/likes").to_return(:body => "{}")
186
+ client.company_updates_likes("company_update_key", :id => 1586).should be_an_instance_of(LinkedIn::Mash)
187
+ end
188
+
189
+ it "should be able to follow a company" do
190
+ stub_request(:post, "https://api.linkedin.com/v1/people/~/following/companies").to_return(:body => "", :status => 201)
191
+
192
+ response = client.follow_company(1586)
193
+ response.body.should == nil
194
+ response.status.should == "201"
195
+ end
196
+
197
+ it "should be able to unfollow a company" do
198
+ stub_request(:delete, "https://api.linkedin.com/v1/people/~/following/companies/id=1586").to_return(:body => "", :status => 201)
199
+
200
+ response = client.unfollow_company(1586)
201
+ response.body.should == nil
202
+ response.status.should == "201"
203
+ end
204
+
205
+ end
206
+
207
+ context "Job API", :vcr do
208
+
209
+ it "should be able to view a job listing" do
210
+ stub_request(:get, "https://api.linkedin.com/v1/jobs/id=1586").to_return(:body => "{}")
211
+ client.job(:id => 1586).should be_an_instance_of(LinkedIn::Mash)
212
+ end
213
+
214
+ it "should be able to view its job bookmarks" do
215
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/job-bookmarks").to_return(:body => "{}")
216
+ client.job_bookmarks.should be_an_instance_of(LinkedIn::Mash)
217
+ end
218
+
219
+ it "should be able to view its job suggestion" do
220
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/suggestions/job-suggestions").to_return(:body => "{}")
221
+ client.job_suggestions.should be_an_instance_of(LinkedIn::Mash)
222
+ end
223
+
224
+ it "should be able to add a bookmark" do
225
+ stub_request(:post, "https://api.linkedin.com/v1/people/~/job-bookmarks").to_return(:body => "", :status => 201)
226
+ response = client.add_job_bookmark(:id => 1452577)
227
+ response.body.should == nil
228
+ response.status.should == "201"
229
+ end
230
+ end
231
+
232
+ context "Group API" do
233
+
234
+ it "should be able to list group memberships for a profile" do
235
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/group-memberships").to_return(:body => "{}")
236
+ client.group_memberships.should be_an_instance_of(LinkedIn::Mash)
237
+ end
238
+
239
+ it "should be able to list suggested groups for a profile" do
240
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/suggestions/groups").to_return(:body => '{"id": "123"}')
241
+ response = client.group_suggestions
242
+ response.id.should == '123'
243
+ end
244
+
245
+ it "should be able to parse nested fields" do
246
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/group-memberships:(group:(id,name,small-logo-url,short-description))").to_return(:body => "{}")
247
+ client.group_memberships(:fields => [{:group => ['id', 'name', 'small-logo-url', 'short-description']}]).should be_an_instance_of(LinkedIn::Mash)
248
+ end
249
+
250
+ it "should be able to join a group" do
251
+ stub_request(:put, "https://api.linkedin.com/v1/people/~/group-memberships/123").to_return(:body => "", :status => 201)
252
+
253
+ response = client.join_group(123)
254
+ response.body.should == nil
255
+ response.status.should == "201"
256
+ end
257
+
258
+ it "should be able to list a group profile" do
259
+ stub_request(:get, "https://api.linkedin.com/v1/groups/123").to_return(:body => '{"id": "123"}')
260
+ response = client.group_profile(:id => 123)
261
+ response.id.should == '123'
262
+ end
263
+
264
+ it "should be able to list group posts" do
265
+ stub_request(:get, "https://api.linkedin.com/v1/groups/123/posts").to_return(:body => '{"id": "123"}')
266
+ response = client.group_posts(:id => 123)
267
+ response.id.should == '123'
268
+ end
269
+
270
+ it 'should be able to post a discussion to a group' do
271
+ expected = {
272
+ 'title' => 'New Discussion',
273
+ 'summary' => 'New Summary',
274
+ 'content' => {
275
+ "submitted-url" => "http://www.google.com"
276
+ }
277
+ }
278
+
279
+ stub_request(:post, "https://api.linkedin.com/v1/groups/123/posts").with(:body => expected).to_return(:body => "", :status => 201)
280
+ response = client.post_group_discussion(123, expected)
281
+ response.body.should == nil
282
+ response.status.should == '201'
283
+ end
284
+
285
+ it "should be able to share a new group status" do
286
+ stub_request(:post, "https://api.linkedin.com/v1/groups/1/posts").to_return(:body => "", :status => 201)
287
+ response = client.add_group_share(1, :comment => "Testing, 1, 2, 3")
288
+ response.body.should == nil
289
+ response.status.should == "201"
290
+ end
291
+ end
292
+
293
+ context "Communication API" do
294
+ it "should be able to send a message" do
295
+ stub_request(:post, "https://api.linkedin.com/v1/people/~/mailbox").to_return(:body => "", :status => 201)
296
+ response = client.send_message("subject", "body", ["recip1", "recip2"])
297
+ response.body.should == nil
298
+ response.status.should == "201"
299
+ end
300
+ end
301
+
302
+ context "Errors" do
303
+ it "should raise AccessDeniedError when LinkedIn returns 403 status code" do
304
+ stub_request(:get, "https://api.linkedin.com/v1/people-search?first-name=Javan").to_return(:body => "{}", :status => 403)
305
+ expect{ client.search(:first_name => "Javan") }.to raise_error(LinkedIn::Errors::AccessDeniedError)
306
+ end
307
+ end
308
+ end