Complete teams meeting creation and token refresh
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
class BroadcastsController < ApplicationController
|
class BroadcastsController < ApplicationController
|
||||||
|
require 'microsoft_graph'
|
||||||
layout "project"
|
layout "project"
|
||||||
|
|
||||||
before_action :set_project
|
before_action :set_project
|
||||||
@@ -17,6 +18,25 @@ class BroadcastsController < ApplicationController
|
|||||||
def create
|
def create
|
||||||
@broadcast.attributes = broadcast_params
|
@broadcast.attributes = broadcast_params
|
||||||
|
|
||||||
|
begin
|
||||||
|
graph_api = MicrosoftGraph.new(
|
||||||
|
current_user,
|
||||||
|
ENV['AZURE_CLIENT_ID'],
|
||||||
|
ENV['AZURE_CLIENT_SECRET'],
|
||||||
|
ENV['AZURE_TENANT_ID'],
|
||||||
|
ENV['AZURE_SCOPES']
|
||||||
|
)
|
||||||
|
|
||||||
|
subject = "#{@broadcast.name} Online Meeting"
|
||||||
|
teams_meeting = graph_api.create_teams_meeting(subject)
|
||||||
|
join_url = teams_meeting['joinUrl']
|
||||||
|
@broadcast.microsoft_teams_meeting_url = join_url if join_url.present?
|
||||||
|
rescue StandardError => e
|
||||||
|
@broadcast.errors[:base] << e.message
|
||||||
|
render :new
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if @broadcast.save
|
if @broadcast.save
|
||||||
log_create_analytics
|
log_create_analytics
|
||||||
redirect_to [@project, :broadcasts], notice: t(".notice")
|
redirect_to [@project, :broadcasts], notice: t(".notice")
|
||||||
@@ -27,7 +47,7 @@ class BroadcastsController < ApplicationController
|
|||||||
|
|
||||||
def show
|
def show
|
||||||
# @conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting]
|
# @conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting]
|
||||||
@conference_url = url_for [@broadcast.project, @broadcast, :microsoft_teams_meeting]
|
@conference_url = @broadcast.microsoft_teams_meeting_url
|
||||||
@recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page])
|
@recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page])
|
||||||
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
|
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
|
||||||
render layout: 'application'
|
render layout: 'application'
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
class MicrosoftTeamsMeetingsController < ApplicationController
|
|
||||||
require 'microsoft_graph'
|
|
||||||
def show
|
|
||||||
authorize broadcast = Broadcast.find(params[:broadcast_id])
|
|
||||||
|
|
||||||
graph_api = MicrosoftGraph.new(current_user, ENV['AZURE_CLIENT_ID'], ENV['AZURE_CLIENT_SECRET'], ENV['AZURE_SCOPES'])
|
|
||||||
|
|
||||||
meeting_start = DateTime.now
|
|
||||||
meeting_end = DateTime.now + 1.hour
|
|
||||||
subject = "Broadcast Meeting"
|
|
||||||
|
|
||||||
|
|
||||||
r = graph_api.create_teams_meeting(meeting_start, meeting_end, subject)
|
|
||||||
|
|
||||||
render plain: r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
class Broadcast < ApplicationRecord
|
class Broadcast < ApplicationRecord
|
||||||
|
require 'microsoft_graph'
|
||||||
include PgSearch
|
include PgSearch
|
||||||
|
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<% end %>
|
<% end %>
|
||||||
<p class="card-text">If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.</p>
|
<p class="card-text">If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.</p>
|
||||||
<%= link_to 'Video Conference', @conference_url, class: 'btn btn-primary btn-block', target: '_blank' %>
|
<%= link_to 'Video Conference', @conference_url, class: "btn btn-primary btn-block #{@conference_url.present? ? '' : 'disabled'}", target: '_blank' %>
|
||||||
</div>
|
</div>
|
||||||
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
|
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
|
||||||
<div id="broadcast_recordings">
|
<div id="broadcast_recordings">
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ Rails.application.routes.draw do
|
|||||||
delete :destroy_file
|
delete :destroy_file
|
||||||
end
|
end
|
||||||
# resource :zoom_meeting, only: [:show]
|
# resource :zoom_meeting, only: [:show]
|
||||||
resource :microsoft_teams_meeting, only: [:show]
|
|
||||||
end
|
end
|
||||||
resources :directories, except: [:index] do
|
resources :directories, except: [:index] do
|
||||||
member do
|
member do
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddMicrosoftTeamsMeetingJoinUrlToBroadcasts < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :broadcasts, :microsoft_teams_meeting_url, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -562,7 +562,8 @@ CREATE TABLE public.broadcasts (
|
|||||||
token character varying,
|
token character varying,
|
||||||
streamer_status integer DEFAULT 0,
|
streamer_status integer DEFAULT 0,
|
||||||
shoot_location_time_zone character varying DEFAULT 'UTC'::character varying,
|
shoot_location_time_zone character varying DEFAULT 'UTC'::character varying,
|
||||||
full_live_stream_playback_uid character varying
|
full_live_stream_playback_uid character varying,
|
||||||
|
microsoft_teams_meeting_url character varying
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -3972,6 +3973,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||||||
('20200721140821'),
|
('20200721140821'),
|
||||||
('20200725231419'),
|
('20200725231419'),
|
||||||
('20200810140331'),
|
('20200810140331'),
|
||||||
('20200812161119');
|
('20200812161119'),
|
||||||
|
('20200817233053');
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,49 +2,29 @@ require 'httparty'
|
|||||||
|
|
||||||
class MicrosoftGraph
|
class MicrosoftGraph
|
||||||
BASE_URL = 'https://graph.microsoft.com/v1.0'.freeze
|
BASE_URL = 'https://graph.microsoft.com/v1.0'.freeze
|
||||||
BETA_BASE_URL = 'https://graph.microsoft.com/beta'.freeze
|
|
||||||
# Documentation says that "common" can be used (instead of tenantID) to refresh access token
|
|
||||||
REFRESH_TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'.freeze
|
|
||||||
|
|
||||||
def initialize(current_user, client_id, client_secret, scopes)
|
def initialize(current_user, client_id, client_secret, tenant_id, scopes)
|
||||||
@current_user = current_user
|
@current_user = current_user
|
||||||
@uid = current_user.microsoft_user_id
|
@uid = current_user.microsoft_user_id
|
||||||
@token = current_user.microsoft_access_token
|
@token = current_user.microsoft_access_token
|
||||||
@refresh_token = current_user.microsoft_refresh_token
|
@refresh_token = current_user.microsoft_refresh_token
|
||||||
@expires_at = current_user.microsoft_token_expires_at
|
@token_expires_at = current_user.microsoft_token_expires_at
|
||||||
|
|
||||||
@client_id = client_id
|
@client_id = client_id
|
||||||
@client_secret = client_secret
|
@client_secret = client_secret
|
||||||
|
@tenant_id = tenant_id
|
||||||
@scopes = scopes
|
@scopes = scopes
|
||||||
end
|
end
|
||||||
|
|
||||||
def request(resource)
|
def create_teams_meeting(subject)
|
||||||
return false unless @token
|
# Obtain new token if token is expired or will expire in less than 5 minutes
|
||||||
|
if 5.minutes.from_now.to_i > @token_expires_at.seconds
|
||||||
response = HTTParty.get(
|
refresh_access_token
|
||||||
"#{BASE_URL}/#{resource}",
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer #{@token}"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.code != 200
|
|
||||||
p '[Microsoft Graph API Error]'
|
|
||||||
p response.inspect
|
|
||||||
false
|
|
||||||
else
|
|
||||||
JSON.parse(response.body)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def create_teams_meeting(start_date_time, end_date_time, subject)
|
|
||||||
# check if token expired and obtain new access_token using refresh token
|
|
||||||
|
|
||||||
response = HTTParty.post(
|
response = HTTParty.post(
|
||||||
"#{BETA_BASE_URL}/me/onlineMeetings",
|
"#{BASE_URL}/me/onlineMeetings",
|
||||||
body: {
|
body: {
|
||||||
startDateTime: start_date_time,
|
|
||||||
endDateTime: end_date_time,
|
|
||||||
subject: subject,
|
subject: subject,
|
||||||
participants: {
|
participants: {
|
||||||
organizer: {
|
organizer: {
|
||||||
@@ -55,22 +35,35 @@ class MicrosoftGraph
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}.to_json,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "Bearer #{@token}"
|
Authorization: "Bearer #{@token}",
|
||||||
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
raise StandardError, 'Authenticated user does not have a permission to create Teams Online Meeting' if response.code == 403
|
||||||
|
|
||||||
if response.code != 201
|
if response.code != 201
|
||||||
p '[Microsoft Graph API Error] Failed to create online meeting'
|
Rails.logger.error('[Microsoft Graph Error]')
|
||||||
p response.inspect
|
Rails.logger.error(response.inspect)
|
||||||
|
raise StandardError, "Failed to create teams meeting [#{response.code}]"
|
||||||
else
|
else
|
||||||
JSON.parse(response.body)
|
JSON.parse(response.body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def refresh_token
|
private
|
||||||
response = HTTParty.post(REFRESH_TOKEN_URL,
|
|
||||||
|
def refresh_token_url
|
||||||
|
"https://login.microsoftonline.com/#{@tenant_id}/oauth2/v2.0/token"
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_access_token
|
||||||
|
response = HTTParty.post(refresh_token_url,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
body: {
|
body: {
|
||||||
client_id: @client_id,
|
client_id: @client_id,
|
||||||
client_secret: @client_secret,
|
client_secret: @client_secret,
|
||||||
@@ -79,5 +72,26 @@ class MicrosoftGraph
|
|||||||
scope: @scopes
|
scope: @scopes
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if response.code != 200
|
||||||
|
Rails.logger.error '[Microsoft Graph Error] Failed to obtain new access token using refresh token'
|
||||||
|
Rails.logger.error(response.inspect)
|
||||||
|
raise StandardError, 'Failed to obtain new access token'
|
||||||
|
end
|
||||||
|
|
||||||
|
parsed_response = JSON.parse(response.body)
|
||||||
|
|
||||||
|
new_access_token = parsed_response['access_token']
|
||||||
|
new_refresh_token = parsed_response['refresh_token']
|
||||||
|
token_expires_in = parsed_response['expires_in'] # For how long access token is valid (in seconds)
|
||||||
|
token_new_expiration_time = Time.now.to_i + token_expires_in
|
||||||
|
|
||||||
|
@current_user.microsoft_access_token = new_access_token
|
||||||
|
@current_user.microsoft_refresh_token = new_refresh_token
|
||||||
|
@current_user.microsoft_token_expires_at = token_new_expiration_time
|
||||||
|
@current_user.save!
|
||||||
|
|
||||||
|
@token = new_access_token
|
||||||
|
@refresh_token = new_refresh_token
|
||||||
|
@token_expires_at = token_new_expiration_time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Reference in New Issue
Block a user