xing_api_client 0.0.1

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 (53) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +76 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +239 -0
  8. data/Rakefile +1 -0
  9. data/config.yml.sample +7 -0
  10. data/lib/xing_api_client/call/base.rb +23 -0
  11. data/lib/xing_api_client/call/registry.rb +22 -0
  12. data/lib/xing_api_client/call/users_bookmarks_call.rb +36 -0
  13. data/lib/xing_api_client/call/users_call.rb +11 -0
  14. data/lib/xing_api_client/call/users_contact_requests_call.rb +41 -0
  15. data/lib/xing_api_client/call/users_contacts_call.rb +25 -0
  16. data/lib/xing_api_client/call/users_contacts_shared_call.rb +24 -0
  17. data/lib/xing_api_client/call/users_contacts_tags_call.rb +14 -0
  18. data/lib/xing_api_client/call/users_find_by_emails_call.rb +21 -0
  19. data/lib/xing_api_client/call/users_me_id_card_call.rb +9 -0
  20. data/lib/xing_api_client/call/users_network_paths_call.rb +18 -0
  21. data/lib/xing_api_client/call/users_profile_message_call.rb +22 -0
  22. data/lib/xing_api_client/call.rb +12 -0
  23. data/lib/xing_api_client/config.rb +38 -0
  24. data/lib/xing_api_client/object/address.rb +8 -0
  25. data/lib/xing_api_client/object/base.rb +23 -0
  26. data/lib/xing_api_client/object/company.rb +15 -0
  27. data/lib/xing_api_client/object/school.rb +15 -0
  28. data/lib/xing_api_client/object/user.rb +29 -0
  29. data/lib/xing_api_client/object/year_month.rb +28 -0
  30. data/lib/xing_api_client/object.rb +11 -0
  31. data/lib/xing_api_client/request/error.rb +23 -0
  32. data/lib/xing_api_client/request.rb +85 -0
  33. data/lib/xing_api_client/version.rb +3 -0
  34. data/lib/xing_api_client.rb +54 -0
  35. data/spec/spec_helper.rb +107 -0
  36. data/spec/xing_api_client/call/users_bookmarks_call_spec.rb +103 -0
  37. data/spec/xing_api_client/call/users_call_spec.rb +41 -0
  38. data/spec/xing_api_client/call/users_contact_requests_call_spec.rb +130 -0
  39. data/spec/xing_api_client/call/users_contacts_call_spec.rb +45 -0
  40. data/spec/xing_api_client/call/users_contacts_shared_call_spec.rb +45 -0
  41. data/spec/xing_api_client/call/users_contacts_tags_call_spec.rb +20 -0
  42. data/spec/xing_api_client/call/users_find_by_emails_call_spec.rb +37 -0
  43. data/spec/xing_api_client/call/users_me_id_card_call_spec.rb +19 -0
  44. data/spec/xing_api_client/call/users_network_paths_call_spec.rb +37 -0
  45. data/spec/xing_api_client/call/users_profile_message_call_spec.rb +63 -0
  46. data/spec/xing_api_client/config_spec.rb +67 -0
  47. data/spec/xing_api_client/object/user_spec.rb +37 -0
  48. data/spec/xing_api_client/object/year_month_spec.rb +70 -0
  49. data/spec/xing_api_client/request/error_spec.rb +15 -0
  50. data/spec/xing_api_client/request_spec.rb +70 -0
  51. data/spec/xing_api_client_spec.rb +51 -0
  52. data/xing_api_client.gemspec +26 -0
  53. metadata +227 -0
@@ -0,0 +1,38 @@
1
+ class XingApiClient
2
+ module Config
3
+ DEFAULT_SETTINGS = {
4
+ host: 'https://api.xing.com',
5
+ request_token_path: '/v1/request_token',
6
+ authorize_path: '/v1/authorize',
7
+ access_token_path: '/v1/access_token',
8
+ consumer_key: nil,
9
+ consumer_secret: nil,
10
+ callback_url: nil
11
+ }
12
+
13
+ @settings = ::OpenStruct.new(DEFAULT_SETTINGS)
14
+
15
+ def self.set(hash)
16
+ @settings = ::OpenStruct.new(DEFAULT_SETTINGS.merge(hash))
17
+ end
18
+
19
+ def self.load_file(path)
20
+ set YAML.load_file(path)
21
+ end
22
+
23
+ def self.load_env(prefix = nil)
24
+ prefix ||= self.to_s.split('::').first.upcase
25
+
26
+ hash = {}
27
+ DEFAULT_SETTINGS.keys.each do |key|
28
+ hash[key] = ENV["#{prefix}_#{key.upcase}"] || DEFAULT_SETTINGS[key]
29
+ end
30
+ set hash
31
+ end
32
+
33
+ private
34
+ def config
35
+ XingApiClient::Config.instance_variable_get('@settings')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,8 @@
1
+ class XingApiClient
2
+ class Object
3
+ class Address
4
+ include Base
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ class XingApiClient
2
+ class Object
3
+ module Base
4
+ def initialize(data)
5
+ @data = data.with_indifferent_access
6
+ end
7
+
8
+ def method_missing(method_name, *args, &block)
9
+ if @data.has_key? method_name
10
+ eigenclass = class << self; self; end
11
+ eigenclass.class_eval do
12
+ define_method(method_name) do
13
+ @data[method_name]
14
+ end
15
+ end
16
+ send(method_name, *args, &block)
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ class XingApiClient
2
+ class Object
3
+ class Company
4
+ include Base
5
+
6
+ def begin_date
7
+ @begin_date ||= XingApiClient::Object::YearMonth.new(@data['begin_date'])
8
+ end
9
+
10
+ def end_date
11
+ @end_date ||= XingApiClient::Object::YearMonth.new(@data['end_date'])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class XingApiClient
2
+ class Object
3
+ class School
4
+ include Base
5
+
6
+ def begin_date
7
+ @begin_date ||= XingApiClient::Object::YearMonth.new(@data['begin_date'])
8
+ end
9
+
10
+ def end_date
11
+ @end_date ||= XingApiClient::Object::YearMonth.new(@data['end_date'])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ class XingApiClient
2
+ class Object
3
+ class User
4
+ include Base
5
+ AVAILABLE_FIELDS = ["id", "first_name", "last_name", "display_name", "page_name", "employment_status", "gender", "birth_date", "active_email", "time_zone", "premium_services", "badges", "wants", "haves", "interests", "organisation_member", "languages", "private_address", "business_address", "web_profiles", "instant_messaging_accounts", "professional_experience", "educational_background", "photo_urls", "permalink"]
6
+
7
+ def private_address
8
+ @private_address ||= XingApiClient::Object::Address.new(@data['private_address'])
9
+ end
10
+
11
+ def business_address
12
+ @business_address ||= XingApiClient::Object::Address.new(@data['business_address'])
13
+ end
14
+
15
+ def professional_experience
16
+ @professional_experience ||= begin
17
+ [XingApiClient::Object::Company.new(@data['professional_experience']['primary_company'])] +
18
+ @data['professional_experience']['non_primary_companies'].map{ |c| XingApiClient::Object::Company.new(c)}
19
+ end
20
+ end
21
+
22
+ def birth_date
23
+ h = @data['birth_date']
24
+
25
+ Date.parse [h['year'],h['month'],h['day']].join('-') rescue nil
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ class XingApiClient
2
+ class Object
3
+ class YearMonth
4
+ require 'time'
5
+
6
+ attr_accessor :year, :month
7
+
8
+ def initialize(value)
9
+ @value = value
10
+ self.year, self.month = @value.to_s.split('-').map{ |s| s.to_i if s}
11
+ end
12
+
13
+ def <=>(an_other)
14
+ to_date <=> an_other.to_date
15
+ end
16
+
17
+ def to_date
18
+ if year.nil?
19
+ Date.new
20
+ elsif month.nil?
21
+ Date.parse("#{year}-1-1")
22
+ else
23
+ Date.parse("#{year}-#{month}-1")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ require 'xing_api_client/object/base'
2
+ require 'xing_api_client/object/user'
3
+ require 'xing_api_client/object/address'
4
+ require 'xing_api_client/object/company'
5
+ require 'xing_api_client/object/school'
6
+ require 'xing_api_client/object/year_month'
7
+
8
+ class XingApiClient
9
+ class Object
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ class XingApiClient
2
+ class Request
3
+ class Error < StandardError
4
+ attr_accessor :code, :api_name, :response
5
+
6
+ def initialize(code, api_name, response)
7
+ @code = code
8
+ @api_name = api_name
9
+ @response = response
10
+ end
11
+
12
+ def to_s
13
+ [code, response.inspect].join(' - ')
14
+ end
15
+ end
16
+
17
+ class InvalidParametersError < Error; end
18
+ class InvalidOauthTokenError < Error; end
19
+ class ThrottlingError < Error; end
20
+ class ResourceNotFoundError < Error; end
21
+ class AccessDeniedError < Error; end
22
+ end
23
+ end
@@ -0,0 +1,85 @@
1
+ class XingApiClient
2
+ class Request
3
+ require 'xing_api_client/request/error'
4
+ require 'parallel'
5
+ include Config
6
+ include XingApiClient::Call
7
+
8
+ ERROR_CLASSES = {
9
+ 'INVALID_PARAMETERS' => InvalidParametersError,
10
+ 'INVALID_OAUTH_TOKEN' => InvalidOauthTokenError,
11
+ 'RATE_LIMIT_EXCEEDED' => ThrottlingError,
12
+ 'USER_NOT_FOUND' => ResourceNotFoundError,
13
+ 403 => AccessDeniedError,
14
+ 404 => ResourceNotFoundError
15
+ }
16
+
17
+ def initialize(consumer_token)
18
+ @consumer_token = consumer_token
19
+ end
20
+
21
+ private
22
+ attr_reader :consumer_token
23
+
24
+ def map_user(data)
25
+ XingApiClient::Object::User.new data
26
+ end
27
+
28
+ def make_request!(verb, url, params = nil, options = {})
29
+ options = { array_keys: [], allowed_codes: [200] }.merge(options)
30
+ url = [config.host, url].join('/')
31
+ request_params = add_default_values(params)
32
+
33
+ result = if verb != :post
34
+ consumer_token.request(verb, url + generate_url_params(request_params))
35
+ else
36
+ consumer_token.request(verb, url, request_params)
37
+ end
38
+
39
+ code = result.code.to_i
40
+
41
+ data = unless result.body.nil?
42
+ if options[:content_type] == 'text'
43
+ result.body
44
+ else
45
+ JSON.parse(result.body)
46
+ end
47
+ end
48
+
49
+ handle_error!(code, data) if not Array(options[:allowed_codes]).include?(code)
50
+
51
+ Array(options[:array_keys]).each { |key| data = data[key] }
52
+
53
+ data
54
+ end
55
+
56
+ def generate_url_params(params)
57
+ '?' + params.to_a.map{ |key, value| "#{key}=#{CGI.escape(value.to_s)}"}.join('&')
58
+ end
59
+
60
+ def add_default_values(params)
61
+ return {} if params.nil? || params.empty?
62
+
63
+ {}.tap do |result|
64
+ params.each_pair do |key, value|
65
+ value = case key
66
+ when :offset
67
+ value.to_i
68
+ when :user_fields
69
+ value || XingApiClient::Object::User::AVAILABLE_FIELDS.join(',')
70
+ else
71
+ value
72
+ end
73
+
74
+ result[key] = value
75
+ end
76
+ end
77
+ end
78
+
79
+ def handle_error!(code, data)
80
+ error_class = ERROR_CLASSES[data.nil? ? code : data['error_name']] || Error
81
+
82
+ raise error_class.new(code, data['error_name'], data)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ class XingApiClient
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,54 @@
1
+ require "oauth"
2
+ require 'json'
3
+ require 'open-uri'
4
+ require 'yaml'
5
+ require 'ostruct'
6
+ require 'active_support/core_ext/hash/indifferent_access'
7
+ %w{ version config object call request}.each{ |name| require "xing_api_client/#{name}"}
8
+
9
+ class XingApiClient
10
+ include Config
11
+ extend Config
12
+
13
+ def initialize(access_token, secret, options = {})
14
+ @access_token = access_token
15
+ @secret = secret
16
+ @consumer = self.class.consumer(options)
17
+ end
18
+
19
+ def self.request_params
20
+ oauth_params = {}
21
+ oauth_params.merge!(oauth_callback: config.callback_url) if config.callback_url
22
+
23
+ token = consumer.get_request_token(oauth_params)
24
+
25
+ { request_token: token, auth_url: token.authorize_url }
26
+ end
27
+
28
+ def self.authorize(token, pin)
29
+ access_token = token.get_access_token(oauth_verifier: pin)
30
+
31
+ { access_token: access_token.token, secret: access_token.secret }
32
+ end
33
+
34
+ def request
35
+ Request.new(consumer_token)
36
+ end
37
+
38
+ def self.consumer(options = {})
39
+ consumer_key = options[:consumer_key] || config.consumer_key
40
+ consumer_secret = options[:consumer_secret] || config.consumer_secret
41
+
42
+ ::OAuth::Consumer.new(consumer_key, consumer_secret, {
43
+ :site => config.host,
44
+ :request_token_path => config.request_token_path,
45
+ :authorize_path => config.authorize_path,
46
+ :access_token_path => config.access_token_path
47
+ })
48
+ end
49
+
50
+ private
51
+ def consumer_token
52
+ OAuth::ConsumerToken.new(@consumer, @access_token, @secret)
53
+ end
54
+ end
@@ -0,0 +1,107 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'xing_api_client'
6
+
7
+
8
+ RSpec.configure do |config|
9
+ # some (optional) config here
10
+ end
11
+
12
+ XingApiClient::Object::User::EXAMPLE_RESPONSE = {
13
+ "users" => [
14
+ {
15
+ "id" => "13802856_c7551a",
16
+ "first_name" => "Firstname",
17
+ "last_name" => "Lastname",
18
+ "display_name" => "Firstname Lastname",
19
+ "page_name" => "Firstname_Lastname8",
20
+ "employment_status" => "EMPLOYEE",
21
+ "gender" => "m",
22
+ "birth_date" => {
23
+ "day" => 3,
24
+ "month" => 1,
25
+ "year" => 1983
26
+ },
27
+ "active_email" => "somemailaddress@gmail.com",
28
+ "time_zone" => {
29
+ "utc_offset" => 2,
30
+ "name" => "Europe/Berlin"
31
+ },
32
+ "premium_services" => [
33
+ "SEARCH",
34
+ "PRIVATEMESSAGES"
35
+ ],
36
+ "badges" => [
37
+ "PREMIUM",
38
+ "MODERATOR"
39
+ ],
40
+ "wants" => "new gems, cool rails stuff, pizza",
41
+ "haves" => "Jenkins CI, MySQL, REST, TDD, UML",
42
+ "interests" => "CSS, CoffeeScript, HTML5, Robotics, jQuery",
43
+ "organisation_member" => nil,
44
+ "languages" => {
45
+ "en" => "FLUENT",
46
+ "de" => "NATIVE"
47
+ },
48
+ "private_address" => {
49
+ "street" => "Somewhere Str. 85",
50
+ "zip_code" => "20756",
51
+ "city" => "Hamburg",
52
+ "province" => "Hamburg",
53
+ "country" => "DE",
54
+ "email" => "somemailaddress@gmail.com",
55
+ "phone" => nil,
56
+ "fax" => nil,
57
+ "mobile_phone" => "49|111|2222222222"
58
+ },
59
+ "business_address" => {
60
+ "street" => "Somewhere Str. 85",
61
+ "zip_code" => "20756",
62
+ "city" => "Hamburg",
63
+ "province" => "Hamburg",
64
+ "country" => "DE",
65
+ "email" => "somemailaddress@gmail.com",
66
+ "phone" => nil,
67
+ "fax" => nil,
68
+ "mobile_phone" => nil
69
+ },
70
+ "web_profiles" => {},
71
+ "instant_messaging_accounts" => {
72
+ "skype" => "username"
73
+ },
74
+ "professional_experience" => {
75
+ "primary_company" => {
76
+ "name" => "COMPANY AG",
77
+ "url" => "https://www.company.com",
78
+ "tag" => "COMPANYAG",
79
+ "company_size" => "501-1000",
80
+ "industry" => "INTERNET",
81
+ "title" => "Engineer",
82
+ "career_level" => nil,
83
+ "description" => "",
84
+ "begin_date" => "2013-04",
85
+ "end_date" => nil
86
+ },
87
+ "non_primary_companies" => [
88
+ ],
89
+ "awards" => []
90
+ },
91
+ "educational_background" => {
92
+ "qualifications" => [
93
+ ],
94
+ "schools" => [
95
+ ]
96
+ },
97
+ "photo_urls" => {
98
+ "large" => "https://x1.xingassets.com/img/users/3/5/1/238ddffce.13802856,9.140x185.jpg",
99
+ "maxi_thumb" => "https://x1.xingassets.com/img/users/3/5/1/238ddffce.13802856,9.70x93.jpg",
100
+ "medium_thumb" => "https://x1.xingassets.com/img/users/3/5/1/238ddffce.13802856,9.57x75.jpg",
101
+ "mini_thumb" => "https://x1.xingassets.com/img/users/3/5/1/238ddffce.13802856,9.18x24.jpg",
102
+ "thumb" => "https://x1.xingassets.com/img/users/3/5/1/238ddffce.13802856,9.30x40.jpg"
103
+ },
104
+ "permalink" => "https://www.xing.com/profile/Firstname_Lastname8"
105
+ }
106
+ ]
107
+ }
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe XingApiClient::Call::UsersBookmarksCall do
4
+ let(:call) { Object.new.tap{ |object| object.extend(XingApiClient::Call::UsersBookmarksCall) } }
5
+
6
+ describe '#get_users_bookmarks' do
7
+ context 'with no options' do
8
+ subject do
9
+ call.
10
+ should_receive(:make_request!).
11
+ with(:get, "v1/users/me/bookmarks", {limit: 100, offset: 0, user_fields: nil}, {array_keys: "bookmarks"}).
12
+ and_return("total" => 202, "items" => [{"id" => 1}])
13
+ call.
14
+ should_receive(:make_request!).
15
+ with(:get, "v1/users/me/bookmarks", {:limit=>100, :offset=>100, :user_fields=>nil}, {:array_keys=>["bookmarks", "items"]}).
16
+ and_return([{"id" => 2}])
17
+ call.
18
+ should_receive(:make_request!).
19
+ with(:get, "v1/users/me/bookmarks", {:limit=>100, :offset=>200, :user_fields=>nil}, {:array_keys=>["bookmarks", "items"]}).
20
+ and_return([{"id" => 3}])
21
+
22
+ call.get_users_bookmarks()
23
+ end
24
+
25
+ its(:size) { should == 3 }
26
+ its(:class) { should == Array }
27
+ its(:total) { should == 202 }
28
+ end
29
+
30
+ context 'with all options' do
31
+ subject do
32
+ call.
33
+ should_receive(:make_request!).
34
+ with(:get, "v1/users/:user_id/bookmarks", {:limit=>13, :offset=>5, :user_fields=>[:id, :page_name]}, {:array_keys=>"bookmarks"}).
35
+ and_return("total" => 202, "items" => [{"id" => 1}])
36
+
37
+
38
+ call.get_users_bookmarks(id: ':user_id', offset: 5, limit: 13, fields: [:id, :page_name])
39
+ end
40
+
41
+ its(:size) { should == 1 }
42
+ its(:class) { should == Array }
43
+ its(:total) { should == 202 }
44
+ end
45
+ end
46
+
47
+ describe '#put_users_bookmarks' do
48
+ context 'with no options' do
49
+ subject do
50
+ call.
51
+ should_receive(:make_request!).
52
+ with(:put, "v1/users/me/bookmarks/:bookmark_id", {}, {:allowed_codes=>204})
53
+
54
+ call.put_users_bookmarks(':bookmark_id')
55
+ end
56
+
57
+ its(:class) { should == NilClass }
58
+ end
59
+
60
+ context 'with all options' do
61
+ subject do
62
+ call.
63
+ should_receive(:make_request!).
64
+ with(:put, "v1/users/:user_id/bookmarks/:bookmark_id", {}, {:allowed_codes=>204})
65
+
66
+ call.put_users_bookmarks(':bookmark_id', id: ':user_id')
67
+ end
68
+
69
+ its(:class) { should == NilClass }
70
+ end
71
+ end
72
+
73
+ describe '#delete_users_bookmarks' do
74
+ context 'with no options' do
75
+ subject do
76
+ call.
77
+ should_receive(:make_request!).
78
+ with(:delete, "v1/users/me/bookmarks/:bookmark_id", {}, {:allowed_codes=>204})
79
+
80
+ call.delete_users_bookmarks(':bookmark_id')
81
+ end
82
+
83
+ its(:class) { should == NilClass }
84
+ end
85
+
86
+ context 'with all options' do
87
+ subject do
88
+ call.
89
+ should_receive(:make_request!).
90
+ with(:delete, "v1/users/:user_id/bookmarks/:bookmark_id", {}, {:allowed_codes=>204})
91
+
92
+ call.delete_users_bookmarks(':bookmark_id', id: ':user_id')
93
+ end
94
+
95
+ its(:class) { should == NilClass }
96
+ end
97
+ end
98
+ end
99
+
100
+
101
+
102
+
103
+
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe XingApiClient::Call::UsersCall do
4
+ let(:call) { Object.new.tap{ |object| object.extend(XingApiClient::Call::UsersCall) } }
5
+
6
+ describe '#get_users' do
7
+ context 'with no options' do
8
+ subject do
9
+ call.
10
+ should_receive(:make_request!).
11
+ with(:get, "v1/users/me", {}, {:array_keys=>"users"}).
12
+ and_return([{'id' => ':user_id'}])
13
+
14
+ call.get_users()
15
+ end
16
+
17
+ its(:class) { should == Hash }
18
+ its(:keys) { should == ['id'] }
19
+ end
20
+
21
+ context 'with all options' do
22
+ subject do
23
+ call.
24
+ should_receive(:make_request!).
25
+ with(:get, "v1/users/:user_id", {}, {:array_keys=>"users"}).
26
+ and_return([{'id' => ':user_id'}])
27
+
28
+
29
+ call.get_users(id: ':user_id')
30
+ end
31
+
32
+ its(:class) { should == Hash }
33
+ its(:keys) { should == ['id'] }
34
+ end
35
+ end
36
+ end
37
+
38
+
39
+
40
+
41
+