yammer-client 0.1.0
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.
- data/.gitignore +29 -0
- data/.travis.yml +9 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +13 -0
- data/LICENSE.md +20 -0
- data/README.md +265 -0
- data/Rakefile +12 -0
- data/certs/tiabas-public.pem +21 -0
- data/lib/yammer.rb +28 -0
- data/lib/yammer/api.rb +10 -0
- data/lib/yammer/api/autocomplete.rb +25 -0
- data/lib/yammer/api/group.rb +78 -0
- data/lib/yammer/api/group_membership.rb +32 -0
- data/lib/yammer/api/message.rb +196 -0
- data/lib/yammer/api/network.rb +21 -0
- data/lib/yammer/api/notification.rb +18 -0
- data/lib/yammer/api/search.rb +28 -0
- data/lib/yammer/api/thread.rb +23 -0
- data/lib/yammer/api/topic.rb +21 -0
- data/lib/yammer/api/user.rb +156 -0
- data/lib/yammer/client.rb +101 -0
- data/lib/yammer/configurable.rb +46 -0
- data/lib/yammer/error.rb +61 -0
- data/lib/yammer/http_connection.rb +184 -0
- data/lib/yammer/identity_map.rb +42 -0
- data/lib/yammer/model.rb +6 -0
- data/lib/yammer/model/base.rb +133 -0
- data/lib/yammer/model/group.rb +13 -0
- data/lib/yammer/model/group_membership.rb +11 -0
- data/lib/yammer/model/message.rb +17 -0
- data/lib/yammer/model/message_body.rb +13 -0
- data/lib/yammer/model/thread.rb +44 -0
- data/lib/yammer/model/user.rb +46 -0
- data/lib/yammer/response.rb +43 -0
- data/lib/yammer/version.rb +18 -0
- data/spec/api/autocomplete_spec.rb +23 -0
- data/spec/api/group_membership_spec.rb +30 -0
- data/spec/api/group_spec.rb +58 -0
- data/spec/api/message_spec.rb +118 -0
- data/spec/api/network_spec.rb +23 -0
- data/spec/api/notification_spec.rb +23 -0
- data/spec/api/search_spec.rb +23 -0
- data/spec/api/thread_spec.rb +23 -0
- data/spec/api/topic_spec.rb +23 -0
- data/spec/api/user_spec.rb +76 -0
- data/spec/client_spec.rb +208 -0
- data/spec/connection_spec.rb +280 -0
- data/spec/error_spec.rb +69 -0
- data/spec/fixtures/group.json +1 -0
- data/spec/fixtures/message.json +1 -0
- data/spec/fixtures/messages_in_thread.json +1 -0
- data/spec/fixtures/portal_thread.json +1 -0
- data/spec/fixtures/private_thread.json +1 -0
- data/spec/fixtures/public_thread.json +1 -0
- data/spec/fixtures/user.json +1 -0
- data/spec/identity_map_spec.rb +108 -0
- data/spec/model/base_spec.rb +155 -0
- data/spec/model/group_membership_spec.rb +27 -0
- data/spec/model/group_spec.rb +44 -0
- data/spec/model/message_spec.rb +45 -0
- data/spec/model/thread_spec.rb +66 -0
- data/spec/model/user_spec.rb +150 -0
- data/spec/response_spec.rb +66 -0
- data/spec/spec_helper.rb +42 -0
- data/yammer.gemspec +29 -0
- metadata +270 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'yammer/configurable'
|
3
|
+
require 'yammer/http_connection'
|
4
|
+
require 'yammer/api'
|
5
|
+
|
6
|
+
module Yammer
|
7
|
+
class Client
|
8
|
+
include Yammer::Configurable
|
9
|
+
include Yammer::Api::User
|
10
|
+
include Yammer::Api::Group
|
11
|
+
include Yammer::Api::GroupMembership
|
12
|
+
include Yammer::Api::Message
|
13
|
+
include Yammer::Api::Thread
|
14
|
+
include Yammer::Api::Topic
|
15
|
+
include Yammer::Api::Network
|
16
|
+
include Yammer::Api::Search
|
17
|
+
include Yammer::Api::Notification
|
18
|
+
include Yammer::Api::Autocomplete
|
19
|
+
|
20
|
+
attr_writer :connection_options
|
21
|
+
attr_accessor :site_url, :http_adapter
|
22
|
+
|
23
|
+
def initialize(opts={})
|
24
|
+
Yammer::Configurable.keys.each do |key|
|
25
|
+
instance_variable_set(:"@#{key}", opts.fetch(key, Yammer.instance_variable_get(:"@#{key}")))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Hash] the options used to setup the http connection
|
30
|
+
def connection_options
|
31
|
+
@connection_options ||= {}
|
32
|
+
end
|
33
|
+
|
34
|
+
# set the url to be used for creating an http connection
|
35
|
+
# @param url [string]
|
36
|
+
def site_url=(url)
|
37
|
+
@connection = nil
|
38
|
+
@site_url = url
|
39
|
+
end
|
40
|
+
|
41
|
+
# (see #request)
|
42
|
+
# @note makes a GET request
|
43
|
+
def get(path, params={})
|
44
|
+
request(:get, path, params)
|
45
|
+
end
|
46
|
+
|
47
|
+
# (see #request)
|
48
|
+
# @note makes a PUT request
|
49
|
+
def put(path, params={})
|
50
|
+
request(:put, path, params)
|
51
|
+
end
|
52
|
+
|
53
|
+
# (see #request)
|
54
|
+
# @note makes a POST request
|
55
|
+
def post(path, params={})
|
56
|
+
request(:post, path, params)
|
57
|
+
end
|
58
|
+
|
59
|
+
# (see #request)
|
60
|
+
# @note makes a DELETE request
|
61
|
+
def delete(path, params={})
|
62
|
+
request(:delete, path, params)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Makes an HTTP request using the provided parameters
|
68
|
+
# @raise [Yammer::Error::Unauthorized]
|
69
|
+
# @param method [string]
|
70
|
+
# @param path [string]
|
71
|
+
# @param params [Hash]
|
72
|
+
# @param opts [Hash]
|
73
|
+
# @return [Yammer::Response]
|
74
|
+
# @!visibility private
|
75
|
+
def request(method, path, params={}, opts={})
|
76
|
+
# Log request here
|
77
|
+
response = connection.send_request(method, path, {
|
78
|
+
:params => params,
|
79
|
+
:headers => {
|
80
|
+
'Authorization' => "Bearer #{@access_token}"
|
81
|
+
}
|
82
|
+
})
|
83
|
+
result = Yammer::Response.new(response)
|
84
|
+
status = result.code
|
85
|
+
case status
|
86
|
+
when 200...400
|
87
|
+
result
|
88
|
+
else
|
89
|
+
message = result.empty? ? '' : result.body[:response][:message]
|
90
|
+
raise Yammer::Error.from_status(status).new(message)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# returns an instance of the http adapter
|
95
|
+
# if none is specified, the default is Yammer::HttpConnection
|
96
|
+
# @!visibility private
|
97
|
+
def connection
|
98
|
+
@connection ||= @http_adapter.new(@site_url, connection_options)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Yammer
|
2
|
+
module Configurable
|
3
|
+
|
4
|
+
ENDPOINT = 'https://www.yammer.com' unless defined? ENDPOINT
|
5
|
+
HTTP_CONNECTION = Yammer::HttpConnection unless defined? HTTP_CONNECTION
|
6
|
+
|
7
|
+
attr_accessor :hostname, :client_id, :client_secret, :access_token, :site_url, :http_adapter,
|
8
|
+
:connection_options
|
9
|
+
|
10
|
+
# Return a hash with the default options
|
11
|
+
# @return [Hash]
|
12
|
+
def self.default_options
|
13
|
+
{
|
14
|
+
:site_url => ENDPOINT,
|
15
|
+
:client_id => ENV['YAMMER_CLIENT_ID'],
|
16
|
+
:client_secret => ENV['YAMMER_CLIENT_SECRET'],
|
17
|
+
:access_token => ENV['YAMMER_ACCESS_TOKEN'],
|
18
|
+
:http_adapter => HTTP_CONNECTION,
|
19
|
+
:connection_options => { :max_redirects => 5, :use_ssl => true }
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array<String>
|
24
|
+
def self.keys
|
25
|
+
Yammer::Configurable.default_options.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Hash]
|
29
|
+
def options
|
30
|
+
Hash[Yammer::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset!
|
34
|
+
Yammer::Configurable.keys.each do |key|
|
35
|
+
instance_variable_set(:"@#{key}", Yammer::Configurable.default_options[key.to_sym])
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convenience method to allow configuration options to be set in a block
|
41
|
+
def configure
|
42
|
+
yield self
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/yammer/error.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Yammer
|
2
|
+
module Error
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def from_status(status=nil)
|
6
|
+
case status
|
7
|
+
when 400
|
8
|
+
BadRequest
|
9
|
+
when 401
|
10
|
+
Unauthorized
|
11
|
+
when 403
|
12
|
+
Forbidden
|
13
|
+
when 404
|
14
|
+
NotFound
|
15
|
+
when 406
|
16
|
+
NotAcceptable
|
17
|
+
when 429
|
18
|
+
RateLimitExceeded
|
19
|
+
when 500
|
20
|
+
InternalServerError
|
21
|
+
when 502
|
22
|
+
BadGateway
|
23
|
+
when 503
|
24
|
+
ServiceUnavailable
|
25
|
+
else
|
26
|
+
ApiError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Raised when Yammer returns unknown HTTP status code
|
32
|
+
class ApiError < StandardError; end
|
33
|
+
|
34
|
+
# Raised when Yammer returns the HTTP status code 400
|
35
|
+
class BadRequest < ApiError; end
|
36
|
+
|
37
|
+
# Raised when Yammer returns the HTTP status code 401
|
38
|
+
class Unauthorized < ApiError; end
|
39
|
+
|
40
|
+
# Raised when Yammer returns the HTTP status code 403
|
41
|
+
class Forbidden < ApiError; end
|
42
|
+
|
43
|
+
# Raised when Yammer returns the HTTP status code 404
|
44
|
+
class NotFound < ApiError; end
|
45
|
+
|
46
|
+
# Raised when Yammer returns the HTTP status code 406
|
47
|
+
class NotAcceptable < ApiError; end
|
48
|
+
|
49
|
+
# Raised when Yammer returns the HTTP status code 429
|
50
|
+
class RateLimitExceeded < ApiError; end
|
51
|
+
|
52
|
+
# Raised when Yammer returns the HTTP status code 500
|
53
|
+
class InternalServerError < ApiError; end
|
54
|
+
|
55
|
+
# Raised when Yammer returns the HTTP status code 502
|
56
|
+
class BadGateway < ApiError; end
|
57
|
+
|
58
|
+
# Raised when Yammer returns the HTTP status code 503
|
59
|
+
class ServiceUnavailable < ApiError; end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
begin
|
2
|
+
require 'net/https'
|
3
|
+
rescue LoadError
|
4
|
+
warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
|
5
|
+
require 'net/http'
|
6
|
+
end
|
7
|
+
require 'zlib'
|
8
|
+
require 'addressable/uri'
|
9
|
+
|
10
|
+
module Yammer
|
11
|
+
class HttpConnection
|
12
|
+
|
13
|
+
class UnhandledHTTPMethodError < StandardError; end
|
14
|
+
class UnsupportedSchemeError < StandardError; end
|
15
|
+
|
16
|
+
NET_HTTP_EXCEPTIONS = [
|
17
|
+
EOFError,
|
18
|
+
Errno::ECONNABORTED,
|
19
|
+
Errno::ECONNREFUSED,
|
20
|
+
Errno::ECONNRESET,
|
21
|
+
Errno::EINVAL,
|
22
|
+
Net::HTTPBadResponse,
|
23
|
+
Net::HTTPHeaderSyntaxError,
|
24
|
+
Net::ProtocolError,
|
25
|
+
SocketError,
|
26
|
+
Zlib::GzipFile::Error,
|
27
|
+
] unless defined? NET_HTTP_EXCEPTIONS
|
28
|
+
|
29
|
+
attr_accessor :config, :scheme, :host, :port, :max_redirects, :ssl,
|
30
|
+
:user_agent, :accept, :max_redirects, :headers, :uri
|
31
|
+
|
32
|
+
def self.default_options
|
33
|
+
{
|
34
|
+
:headers => {
|
35
|
+
'Accept' => 'application/json',
|
36
|
+
'User-Agent' => "Yammer Ruby Gem #{Yammer::Version}"
|
37
|
+
},
|
38
|
+
:ssl => {:verify => true},
|
39
|
+
:max_redirects => 5
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(url, options={})
|
44
|
+
@uri = Addressable::URI.parse(url)
|
45
|
+
self.class.default_options.keys.each do |key|
|
46
|
+
instance_variable_set(:"@#{key}", options.fetch(key, self.class.default_options[key]))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def default_headers
|
51
|
+
self.class.default_options[:headers]
|
52
|
+
end
|
53
|
+
|
54
|
+
def scheme=(scheme)
|
55
|
+
unless ['http', 'https'].include? scheme
|
56
|
+
raise UnsupportedSchemeError.new "#{scheme} is not supported, only http and https"
|
57
|
+
end
|
58
|
+
@scheme = scheme
|
59
|
+
end
|
60
|
+
|
61
|
+
def scheme
|
62
|
+
@scheme ||= @uri.scheme
|
63
|
+
end
|
64
|
+
|
65
|
+
def host
|
66
|
+
@host ||= @uri.host
|
67
|
+
end
|
68
|
+
|
69
|
+
def port
|
70
|
+
_port = ssl? ? 443 : 80
|
71
|
+
@port = @uri.port || _port
|
72
|
+
end
|
73
|
+
|
74
|
+
def absolute_url(path='')
|
75
|
+
"#{scheme}://#{host}#{path}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def ssl?
|
79
|
+
scheme == "https" ? true : false
|
80
|
+
end
|
81
|
+
|
82
|
+
def ssl=(opts)
|
83
|
+
raise "Expected Hash but got #{opts.class.name}" unless opts.is_a?(Hash)
|
84
|
+
@ssl.merge!(opts)
|
85
|
+
end
|
86
|
+
|
87
|
+
def http_connection(opts={})
|
88
|
+
_host = opts[:host] || host
|
89
|
+
_port = opts[:port] || port
|
90
|
+
_scheme = opts[:scheme] || scheme
|
91
|
+
|
92
|
+
@http_client = Net::HTTP.new(_host, _port)
|
93
|
+
|
94
|
+
configure_ssl(@http_client) if _scheme == 'https'
|
95
|
+
|
96
|
+
@http_client
|
97
|
+
end
|
98
|
+
|
99
|
+
def send_request(method, path, opts={})
|
100
|
+
headers = @headers.merge(opts.fetch(:headers, {}))
|
101
|
+
params = opts[:params] || {}
|
102
|
+
query = Addressable::URI.form_encode(params)
|
103
|
+
method = method.to_sym
|
104
|
+
normalized_path = query.empty? ? path : [path, query].join("?")
|
105
|
+
client = http_connection(opts.fetch(:connection_options, {}))
|
106
|
+
|
107
|
+
if (method == :post || method == :put)
|
108
|
+
headers['Content-Type'] ||= 'application/x-www-form-urlencoded'
|
109
|
+
end
|
110
|
+
|
111
|
+
case method
|
112
|
+
when :get, :delete
|
113
|
+
response = client.send(method, normalized_path, headers)
|
114
|
+
when :post, :put
|
115
|
+
response = client.send(method, path, query, headers)
|
116
|
+
else
|
117
|
+
raise UnhandledHTTPMethodError.new("Unsupported HTTP method, #{method}")
|
118
|
+
end
|
119
|
+
|
120
|
+
status = response.code.to_i
|
121
|
+
|
122
|
+
case status
|
123
|
+
when 301, 302, 303, 307
|
124
|
+
unless redirect_limit_reached?
|
125
|
+
if status == 303
|
126
|
+
method = :get
|
127
|
+
params = nil
|
128
|
+
headers.delete('Content-Type')
|
129
|
+
end
|
130
|
+
redirect_uri = Addressable::URI.parse(response.header['Location'])
|
131
|
+
conn = {
|
132
|
+
:scheme => redirect_uri.scheme,
|
133
|
+
:host => redirect_uri.host,
|
134
|
+
:port => redirect_uri.port
|
135
|
+
}
|
136
|
+
return send_request(method, redirect_uri.path, :params => params, :headers => headers, :connection_options => conn)
|
137
|
+
end
|
138
|
+
when 100..599
|
139
|
+
@redirect_count = 0
|
140
|
+
else
|
141
|
+
raise "Unhandled status code value of #{response.code}"
|
142
|
+
end
|
143
|
+
response
|
144
|
+
rescue *NET_HTTP_EXCEPTIONS
|
145
|
+
raise "Error::ConnectionFailed, #{$!.backtrace}"
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def configure_ssl(http)
|
151
|
+
http.use_ssl = true
|
152
|
+
http.verify_mode = ssl_verify_mode
|
153
|
+
http.cert_store = ssl_cert_store
|
154
|
+
|
155
|
+
http.cert = ssl[:client_cert] if ssl[:client_cert]
|
156
|
+
http.key = ssl[:client_key] if ssl[:client_key]
|
157
|
+
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
158
|
+
http.ca_path = ssl[:ca_path] if ssl[:ca_path]
|
159
|
+
http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
160
|
+
http.ssl_version = ssl[:version] if ssl[:version]
|
161
|
+
end
|
162
|
+
|
163
|
+
def ssl_verify_mode
|
164
|
+
if ssl.fetch(:verify, true)
|
165
|
+
OpenSSL::SSL::VERIFY_PEER
|
166
|
+
else
|
167
|
+
OpenSSL::SSL::VERIFY_NONE
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def ssl_cert_store
|
172
|
+
return ssl[:cert_store] if ssl[:cert_store]
|
173
|
+
cert_store = OpenSSL::X509::Store.new
|
174
|
+
cert_store.set_default_paths
|
175
|
+
cert_store
|
176
|
+
end
|
177
|
+
|
178
|
+
def redirect_limit_reached?
|
179
|
+
@redirect_count ||= 0
|
180
|
+
@redirect_count += 1
|
181
|
+
@redirect_count > @max_redirects
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Yammer
|
2
|
+
class IdentityMap
|
3
|
+
|
4
|
+
class InvalidKeyError < StandardError; end
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@map = {}
|
8
|
+
@size = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
# @note retrives key from identity map
|
12
|
+
# @return [Hash]
|
13
|
+
# @param key [string]
|
14
|
+
# @param default [Hash]
|
15
|
+
def get(key, default=nil)
|
16
|
+
@map["#{key}"] || default
|
17
|
+
end
|
18
|
+
|
19
|
+
# @note inserts a hash of attributes into identity map
|
20
|
+
# @return [Hash]
|
21
|
+
# @param key [string]
|
22
|
+
# @param value [Hash]
|
23
|
+
def put(key, value)
|
24
|
+
if key.nil? || key.empty?
|
25
|
+
raise InvalidKeyError.new
|
26
|
+
end
|
27
|
+
@map["#{key}"] = value
|
28
|
+
end
|
29
|
+
|
30
|
+
# @note returns the current size of identity map
|
31
|
+
# @return [Integer]
|
32
|
+
def size
|
33
|
+
@map.keys.count
|
34
|
+
end
|
35
|
+
|
36
|
+
# clears the entire identity map
|
37
|
+
# @return [Hash]
|
38
|
+
def purge!
|
39
|
+
@map = {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|