platforms-yammer 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.
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