platforms-yammer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +157 -0
  4. data/Rakefile +18 -0
  5. data/lib/generators/platforms/yammer/install/USAGE +11 -0
  6. data/lib/generators/platforms/yammer/install/install_generator.rb +29 -0
  7. data/lib/generators/platforms/yammer/install/templates/platforms_yammer.rb +4 -0
  8. data/lib/platforms/yammer.rb +15 -0
  9. data/lib/platforms/yammer/api.rb +39 -0
  10. data/lib/platforms/yammer/api/base.rb +20 -0
  11. data/lib/platforms/yammer/api/group_memberships.rb +35 -0
  12. data/lib/platforms/yammer/api/groups.rb +22 -0
  13. data/lib/platforms/yammer/api/invitations.rb +23 -0
  14. data/lib/platforms/yammer/api/messages.rb +165 -0
  15. data/lib/platforms/yammer/api/networks.rb +22 -0
  16. data/lib/platforms/yammer/api/oauth.rb +28 -0
  17. data/lib/platforms/yammer/api/open_graph_objects.rb +29 -0
  18. data/lib/platforms/yammer/api/pending_attachments.rb +34 -0
  19. data/lib/platforms/yammer/api/relationships.rb +39 -0
  20. data/lib/platforms/yammer/api/search.rb +22 -0
  21. data/lib/platforms/yammer/api/streams.rb +21 -0
  22. data/lib/platforms/yammer/api/subscriptions.rb +53 -0
  23. data/lib/platforms/yammer/api/suggestions.rb +31 -0
  24. data/lib/platforms/yammer/api/supervisor_mode.rb +22 -0
  25. data/lib/platforms/yammer/api/threads.rb +23 -0
  26. data/lib/platforms/yammer/api/topics.rb +23 -0
  27. data/lib/platforms/yammer/api/uploaded_files.rb +22 -0
  28. data/lib/platforms/yammer/api/users.rb +109 -0
  29. data/lib/platforms/yammer/authentication.rb +233 -0
  30. data/lib/platforms/yammer/client.rb +228 -0
  31. data/lib/platforms/yammer/configuration.rb +76 -0
  32. data/lib/platforms/yammer/engine.rb +27 -0
  33. data/lib/platforms/yammer/omni_auth_setup.rb +31 -0
  34. data/lib/platforms/yammer/railtie.rb +19 -0
  35. data/lib/platforms/yammer/version.rb +7 -0
  36. data/lib/tasks/platforms/yammer_tasks.rake +4 -0
  37. metadata +280 -0
@@ -0,0 +1,233 @@
1
+ require "platforms/core"
2
+
3
+ module Platforms
4
+ module Yammer
5
+ # A module for Controllers to include so that the OAuth2 flow
6
+ # can be correctly captured from OmniAuth.
7
+ #
8
+ # The {#save_identity} method will set instance variables in the Controller
9
+ # for:
10
+ # * @token, which should be saved in the session for making
11
+ # subsequent API requests.
12
+ # * @platforms_user, which identifies the authenticated {Platforms::User}
13
+ # * @platforms_network, the {Platforms::Network} that the authenticated
14
+ # user belongs to
15
+ # * @switch_network_ids, an Array of {Platforms::Network} ids that the
16
+ # user can switch to (subject to API permissions).
17
+ #
18
+ # @example Include in a controller
19
+ # # app/controllers/my_controller.rb
20
+ #
21
+ # class MyController < ApplcationController
22
+ # include Platforms::Yammer::Authentication
23
+ #
24
+ # before_action :set_token
25
+ #
26
+ # ...
27
+ #
28
+ # def accept
29
+ # save_identity
30
+ # ...
31
+ # end
32
+ #
33
+ # end
34
+ #
35
+ # @author Benjamin Elias
36
+ # @since 0.1.0
37
+ module Authentication
38
+ extend ActiveSupport::Concern
39
+
40
+ include Platforms::Core::OAuth2
41
+
42
+ included do
43
+ # The token to use for {Client} requests
44
+ attr_reader :token
45
+
46
+ # The Platforms::Network of the authenticated user
47
+ attr_reader :platforms_network
48
+
49
+ # The Platforms::User of the authenticated user
50
+ attr_reader :platforms_user
51
+
52
+ # Possible Platforms::Network to switch to (useful later)
53
+ attr_reader :switch_network_ids
54
+ end
55
+
56
+ # Set the token within the class
57
+ #
58
+ # A convenience method, as the token is available in request.env
59
+ # @return [String] the token from OmniAuth
60
+ def set_token
61
+ @token ||= request.env["omniauth.auth"].credentials.token
62
+ end
63
+
64
+ # The Faraday-based client to make REST requests
65
+ #
66
+ # This is required for {#save_identity} to get additional information
67
+ # about the network context.
68
+ # @return [Platforms::Yammer::Client] the client able to be used
69
+ def client
70
+ @client ||= Client.new @token
71
+ end
72
+
73
+ # Save the current identity
74
+ #
75
+ # Note that OmniAuth::Yammer effectively delivers /users/current.json
76
+ # as the extra.raw_info. Rather than delivering the original
77
+ # /oauth2/access_token response. Therefore we need to make another
78
+ # call to /networks/current.json to get the list of (known) networks.
79
+ #
80
+ # @param groups [Boolean] Whether or not to save Group memberships
81
+ # @return [Platforms::User] The identity of the authenticated user
82
+ def save_identity groups=true
83
+ omni = request.env["omniauth.auth"]
84
+ raw_info = omni.extra.raw_info
85
+
86
+ # Get the network information, conveniently for all known networks
87
+ # (should return an array)
88
+ networks_response = client.networks.current
89
+
90
+ current_network = nil
91
+ networks = networks_response.body
92
+
93
+ @switch_network_ids ||= []
94
+
95
+ # Create a Platforms::Network, or update it with current details
96
+ # if it already exists
97
+ networks.each do |network|
98
+ n = Platforms::Network.find_or_initialize_by(
99
+ platform_id: network["id"]
100
+ )
101
+ n.name = network["name"]
102
+ n.trial = bool_safe network["is_freemium"]
103
+ n.permalink = network["permalink"]
104
+ n.platform_type = "yammer"
105
+ n.save!
106
+
107
+ @switch_network_ids << n.id
108
+ # Set @platform_network to the filtered network
109
+ @platforms_network = n if raw_info.network_id.to_s == n.platform_id
110
+ end
111
+
112
+ user = Platforms::User.find_or_initialize_by(
113
+ platform_id: omni.uid,
114
+ platforms_network: @platforms_network
115
+ )
116
+ user.name = omni.info.name
117
+ user.thumbnail_url = omni.info.image
118
+ user.admin = bool_safe raw_info.admin
119
+ user.web_url = omni.info.urls.yammer
120
+ user.email = omni.info.email
121
+ user.save!
122
+ @platforms_user = user
123
+
124
+ # Switch for including groups
125
+ if groups
126
+ current_user = client.users.
127
+ current({include_group_memberships: true})
128
+ sync_groups(current_user.body)
129
+ end
130
+
131
+ # Return @platforms_user
132
+ @platforms_user
133
+ end
134
+
135
+ # Switch the current identity
136
+ #
137
+ # This does much the same as {#save_identity}, but the inputs are
138
+ # very different.
139
+ # This does not use the OmniAuth flow, but instead makes a
140
+ # call to the authenticated API to get the required information.
141
+ #
142
+ # @param client [Client] The authenticated client
143
+ # @param permalink [String] The network permalink to switch to
144
+ # @param groups [Boolean] Whether or not to save Group memberships
145
+ # @return [Platforms::User] The identity of the authenticated user, after
146
+ # switching.
147
+ def switch_identity client, permalink, groups=true
148
+
149
+ # Do not use @client as that uses the token from OmniAuth.
150
+ # switch_identity is designed to be used later in the user flow.
151
+ tokens = client.oauth.tokens.body
152
+ switch_network = tokens.find{ |t| t["network_permalink"] == permalink }
153
+
154
+ # Raise error if the token is not found
155
+ if switch_network.nil?
156
+ raise ActiveRecord::RecordNotFound.new "Couldn't find Platforms::Network for permalink #{permalink}"
157
+ end
158
+
159
+ # Create a new Client with the updated token
160
+ @token = switch_network["token"]
161
+ switch_client = Platforms::Yammer::Client.new @token
162
+
163
+ # Don't worry about updating Network here, should be done at first login.
164
+ @platforms_network = Platforms::Network.find_by!(
165
+ permalink: permalink,
166
+ platform_type: "yammer"
167
+ )
168
+
169
+ # Switch for including groups
170
+ current_options = groups ? { include_group_memberships: true } : {}
171
+ switch_user = switch_client.users.current(current_options).body
172
+
173
+ user = Platforms::User.find_or_initialize_by(
174
+ platform_id: switch_network["user_id"],
175
+ platforms_network: @platforms_network
176
+ )
177
+ user.name = switch_user["full_name"]
178
+ user.thumbnail_url = switch_user["mugshot_url"]
179
+ user.admin = bool_safe switch_user["admin"]
180
+ user.web_url = switch_user["web_url"]
181
+ user.email = switch_user["email"]
182
+ user.save!
183
+ @platforms_user = user
184
+
185
+ sync_groups(switch_user) if groups
186
+
187
+ # Return @platforms_user
188
+ @platforms_user
189
+ end
190
+
191
+ private
192
+
193
+ def sync_groups api_groups
194
+ # Expect data to have group_memberships key
195
+ unless api_groups.has_key?("group_memberships")
196
+ Rails.logger.warn "No Group membership data from users/current.json"
197
+ return
198
+ end
199
+
200
+ membership = @platforms_user.platforms_group_members.pluck(:id)
201
+
202
+ api_groups.fetch("group_memberships",[]).each do |g|
203
+ role = g["admin"] ? "admin" : "member"
204
+
205
+ group = Platforms::Group.find_or_initialize_by(
206
+ platform_id: g["id"].to_s,
207
+ platforms_network: @platforms_network
208
+ )
209
+ group.name = g["full_name"]
210
+ group.web_url = g["web_url"]
211
+ group.save!
212
+
213
+ member = Platforms::GroupMember.find_or_initialize_by(
214
+ platforms_user: @platforms_user,
215
+ platforms_group: group
216
+ )
217
+ member.role = role
218
+ member.save!
219
+
220
+ membership.delete member.id
221
+ end
222
+
223
+ group_members = @platforms_user.platforms_group_members.
224
+ where(id: membership)
225
+
226
+ group_members.each do |d|
227
+ d.destroy!
228
+ end
229
+ end
230
+
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,228 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'platforms/yammer/api'
4
+
5
+ module Platforms
6
+ module Yammer
7
+ # A REST client for Yammer
8
+ #
9
+ # Assumes all of the OAuth2 authentication has been done previously,
10
+ # and a valid token is available.
11
+ #
12
+ # Uses Faraday to conduct HTTP requests and receive responses, with
13
+ # OAuth2 FaradayMiddleWare.
14
+ #
15
+ # @author Benjamin Elias
16
+ # @since 0.1.0
17
+ class Client
18
+
19
+ # Giving access to the underlying Faraday connection allows setting
20
+ # headers and other more detailed configuration.
21
+ # @return [Faraday::Connection] the Faraday connection
22
+ attr_reader :connection
23
+
24
+ # Initialize class with a token
25
+ #
26
+ # Construct a new Faraday {#connection} which uses the token provided.
27
+ # Uses OAuth2 with Bearer authentication.
28
+ #
29
+ # Requests are sent with JSON encoding by default, and responses
30
+ # are parsed as JSON if the Content-type header of the response
31
+ # is set accordingly.
32
+ #
33
+ # To change the default base URL, use the {Configuration} settings
34
+ # in an initializer.
35
+ #
36
+ # Connection configuration can be performed using the yield block,
37
+ # or can be done through the {#connection} variable as shown in the
38
+ # examples.
39
+ #
40
+ # To override default connection headers with request-specific headers,
41
+ # use the headers parameter in any of the request functions.
42
+ #
43
+ # Errors can be turned off if the application explicitly checks the
44
+ # HTTP status codes. By default these are left on.
45
+ #
46
+ # @example Configure Faraday at initialization
47
+ # client = Client.new(token) do |f|
48
+ # f.use(Faraday::Response::RaiseError)
49
+ # end
50
+ # @example Configure Faraday after initialization
51
+ # client = Client.new(token)
52
+ # client.connection.use(Faraday::Response::RaiseError)
53
+ #
54
+ # @param token [String] the token to use
55
+ # @param errors [Boolean] whether to raise errors for non-2XX responses
56
+ # @yieldparam f [::Faraday] the new Faraday connection
57
+ def initialize token, errors=true, &block
58
+ api_base = Platforms::Yammer.configuration.api_base
59
+
60
+ @connection = ::Faraday.new api_base do |faraday|
61
+ faraday.request :oauth2, token, token_type: 'bearer'
62
+ faraday.request :json
63
+ faraday.response :json, :content_type => /\bjson$/
64
+ faraday.use Faraday::Response::RaiseError if errors
65
+ block.call(faraday) if block_given?
66
+ end
67
+ end
68
+
69
+ # Generic request, can be used for new, updated, or
70
+ # undocumented endpoints.
71
+ # @param method [Symbol] The HTTP method (get, post, put, delete)
72
+ # @param endpoint [String] The API endpoint
73
+ # @param options [Hash] Options for the request
74
+ # @param headers [Hash] Headers to include with the request
75
+ # @return [Faraday::Response] the Faraday response
76
+ def request method, endpoint, options={}, headers={}
77
+ connection.send(method, endpoint, options, headers)
78
+ end
79
+
80
+ # Create new {Api::Messages} using {#connection}
81
+ # @example Get messages
82
+ # client.messages.get
83
+ # @return [Api::Messages] the API class
84
+ def messages
85
+ Api::Messages.new @connection
86
+ end
87
+
88
+ # Create new {Api::PendingAttachments} using {#connection}
89
+ # @example Add pending attachment
90
+ # client.pending_attachments.post
91
+ # @return [Api::PendingAttachments] the API class
92
+ def pending_attachments
93
+ Api::PendingAttachments.new @connection
94
+ end
95
+
96
+ # Create new {Api::UploadedFiles} using {#connection}
97
+ # @example Delete an uploaded file
98
+ # client.uploaded_files.delete
99
+ # @return [Api::UploadedFiles] the API class
100
+ def uploaded_files
101
+ Api::UploadedFiles.new @connection
102
+ end
103
+
104
+ # Create new {Api::Threads} using {#connection}
105
+ # @example Get threads
106
+ # client.threads.get
107
+ # @return [Api::Threads] the API class
108
+ def threads
109
+ Api::Threads.new @connection
110
+ end
111
+
112
+ # Create new {Api::Topics} using {#connection}
113
+ # @example Get topics
114
+ # client.topics.get
115
+ # @return [Api::Topics] the API class
116
+ def topics
117
+ Api::Topics.new @connection
118
+ end
119
+
120
+ # Create new {Api::GroupMemberships} using {#connection}
121
+ # @example Add a User to a Group
122
+ # client.group_memberships.post
123
+ # @return [Api::GroupMemberships] the API class
124
+ def group_memberships
125
+ Api::GroupMemberships.new @connection
126
+ end
127
+
128
+ # Create new {Api::Groups} using {#connection}
129
+ # @example Get groups for a user
130
+ # client.groups.for_user
131
+ # @return [Api::Groups] the API class
132
+ def groups
133
+ Api::Groups.new @connection
134
+ end
135
+
136
+ # Create new {Api::Users} using {#connection}
137
+ # @example Get users
138
+ # client.users.get_users
139
+ # @return [Api::Users] the API class
140
+ def users
141
+ Api::Users.new @connection
142
+ end
143
+
144
+ # Create new {Api::Relationships} using {#connection}
145
+ # @example Get user relationships
146
+ # client.relationships.get
147
+ # @return [Api::Relationships] the API class
148
+ def relationships
149
+ Api::Relationships.new @connection
150
+ end
151
+
152
+ # Create new {Api::Streams} using {#connection}
153
+ # @example Get notifications
154
+ # client.streams.notifications
155
+ # @return [Api::Streams] the API class
156
+ def streams
157
+ Api::Streams.new @connection
158
+ end
159
+
160
+ # Create new {Api::Suggestions} using {#connection}
161
+ # @example Get suggestions
162
+ # client.suggestions.get
163
+ # @return [Api::Suggestions] the API class
164
+ def suggestions
165
+ Api::Suggestions.new @connection
166
+ end
167
+
168
+ # Create new {Api::Subscriptions} using {#connection}
169
+ # @example Get subscriptions to a user
170
+ # client.subscriptions.to_user
171
+ # @return [Api::Subscriptions] the API class
172
+ def subscriptions
173
+ Api::Subscriptions.new @connection
174
+ end
175
+
176
+ # Create new {Api::Invitations} using {#connection}
177
+ # @example Send an invitation
178
+ # client.invitations.post
179
+ # @return [Api::Invitations] the API class
180
+ def invitations
181
+ Api::Invitations.new @connection
182
+ end
183
+
184
+ # Create new {Api::Search} using {#connection}
185
+ # @example Search Yammer
186
+ # client.search.get
187
+ # @return [Api::Search] the API class
188
+ def search
189
+ Api::Search.new @connection
190
+ end
191
+
192
+ # Create new {Api::Networks} using {#connection}
193
+ # @example Get current Network
194
+ # client.networks.current
195
+ # @return [Api::Networks] the API class
196
+ def networks
197
+ Api::Networks.new @connection
198
+ end
199
+
200
+ # Create new {Api::OpenGraphObjects} using {#connection}
201
+ # @example Get OG objects
202
+ # client.open_graph_objects.get
203
+ # @return [Api::OpenGraphObjets] the API class
204
+ def open_graph_objects
205
+ Api::OpenGraphObjects.new @connection
206
+ end
207
+
208
+ # Create new {Api::SupervisorMode} using {#connection}
209
+ # @example Toggle supervisor mode
210
+ # client.supervisor_mode.toggle
211
+ # @see https://developer.yammer.com/docs/api-requests
212
+ # @return [Api::SupervisorMode] the API class
213
+ def supervisor_mode
214
+ Api::SupervisorMode.new @connection
215
+ end
216
+
217
+ # Create new {Api::Oauth} using {#connection}
218
+ # @example Get OAuth2 tokens
219
+ # client.oauth.tokens
220
+ # @see https://developer.yammer.com/docs/impersonation
221
+ # @return [Api::Oauth] the API class
222
+ def oauth
223
+ Api::Oauth.new @connection
224
+ end
225
+
226
+ end
227
+ end
228
+ end