Compare commits

...

2 Commits

Author SHA1 Message Date
Bilal
1d29f14953 add conference options to the broadcasts; implement auth to microsoft 2020-08-20 13:09:10 +03:00
Bilal
4b5238435a structure 2020-08-20 12:01:41 +03:00
25 changed files with 511 additions and 61 deletions

View File

@@ -32,3 +32,10 @@ CUSTOM_API_TOKEN=
# Required for simulcasting to Millicast for director mode
MILLICAST_API_SECRET=
MILLICAST_ACCOUNT_ID=
# Required for Microsoft Azure AD Auth
AZURE_CLIENT_ID = Client App ID
AZURE_CLIENT_SECRET = Client App Secret
AZURE_TENANT_ID = Client App Tenant ID
AZURE_REDIRECT_URI = where microsoft will redirect after login, eg. http://localhost:3000/auth/azure_ad/callback
AZURE_SCOPES = Scopes required for Application, eg. 'openid email profile User.Read offline_access OnlineMeetings.ReadWrite'

View File

@@ -139,6 +139,11 @@ gem 'rack-cors'
# Ruby wrappers for the HubSpot REST API
gem "hubspot-ruby"
# OAuth
gem 'omniauth-oauth2', '~> 1.6'
# OmniAuth CSRF protection
gem 'omniauth-rails_csrf_protection', '~> 0.1.2'
group :development, :test, :review do
# Call "byebug" anywhere in the code to stop execution and get a debugger console
gem "byebug", "~> 11.0.1", platforms: [:mri, :mingw, :x64_mingw]

View File

@@ -220,6 +220,7 @@ GEM
activesupport (>= 4.2.0)
hashdiff (1.0.1)
hashery (2.1.2)
hashie (4.1.0)
hexapdf (0.9.3)
cmdparse (~> 3.0, >= 3.0.3)
geom2d (~> 0.2)
@@ -297,6 +298,7 @@ GEM
money (~> 6.13.2)
railties (>= 3.0)
msgpack (1.3.1)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
nio4r (2.5.2)
@@ -308,6 +310,21 @@ GEM
warden
oath-generators (1.0.1)
oath (>= 0.0.12)
oauth2 (1.4.4)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
omniauth (1.9.1)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
omniauth-oauth2 (1.6.0)
oauth2 (~> 1.1)
omniauth (~> 1.9)
omniauth-rails_csrf_protection (0.1.2)
actionpack (>= 4.2)
omniauth (>= 1.3.1)
parallel (1.19.1)
parity (3.2.0)
parser (2.6.5.0)
@@ -552,6 +569,8 @@ DEPENDENCIES
mux_ruby!
oath (~> 1.1.0)
oath-generators (~> 1.0.1)
omniauth-oauth2 (~> 1.6)
omniauth-rails_csrf_protection (~> 0.1.2)
parity (~> 3.2.0)
pdf-reader (~> 2.1.0)
pdfkit (~> 0.8.2)

View File

@@ -75,7 +75,7 @@ class BroadcastsController < ApplicationController
end
def broadcast_params
params.require(:broadcast).permit(:name, :shoot_location_time_zone, files: [])
params.require(:broadcast).permit(:name, :shoot_location_time_zone, :conference_option, files: [])
end
def set_project
@@ -113,7 +113,7 @@ class BroadcastsController < ApplicationController
end
def conference_url_for(broadcast)
broadcast.video_conference_url_override || url_for([broadcast.project, broadcast, :zoom_meeting])
broadcast.video_conference_url_override || url_for([broadcast.project, broadcast, :conference_meeting])
end
def log_create_analytics

View File

@@ -0,0 +1,22 @@
class CallbacksController < ApplicationController
skip_before_action :require_login
skip_after_action :verify_authorized, except: :index
skip_after_action :verify_policy_scoped, only: :index
skip_before_action :verify_authenticity_token
def create
uid = request.env['omniauth.auth'][:uid]
token_data = request.env['omniauth.auth'][:credentials]
current_user&.tap do |user|
user.microsoft_user_id = uid
user.microsoft_access_token = token_data.token
user.microsoft_refresh_token = token_data.refresh_token
user.microsoft_token_expires_at = token_data.expires_at # Expiration time is returned in seconds
user.save
end
redirect_to profile_path
end
end

View File

@@ -0,0 +1,45 @@
class ConferenceMeetingsController < ApplicationController
require 'microsoft_graph'
def show
authorize broadcast = Broadcast.find(params[:broadcast_id])
case broadcast.conference_option
when 'zoom'
redirect_to broadcast.zoom_meeting_url
when 'ms_teams'
if broadcast.conference_join_url.nil?
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']
if join_url.present?
broadcast.conference_join_url = join_url
broadcast.save
else
raise StandardError, 'Failed to read teams meeting join URL'
end
rescue ActionController::InvalidAuthenticityToken => e
Rails.logger.error(e.message)
redirect_to project_broadcast_url(broadcast.project, broadcast), alert: t('.alerts.not_authenticated')
return
rescue StandardError => e
Rails.logger.error(e.message)
redirect_to project_broadcast_url(broadcast.project, broadcast), alert: t('.alerts.failed_to_join')
return
end
end
redirect_to broadcast.conference_join_url
else
redirect_to project_broadcast_url(broadcast.project, broadcast), alert: t('.alerts.unknown_conference_option')
end
end
end

View File

@@ -1,6 +0,0 @@
class ZoomMeetingsController < ApplicationController
def show
authorize broadcast = Broadcast.find(params[:broadcast_id])
redirect_to broadcast.zoom_meeting_url
end
end

View File

@@ -0,0 +1,17 @@
module BroadcastConferencesHelper
def options_for_conference_select
[
['Zoom', 'zoom'],
['MS Teams', 'ms_teams']
]
end
def conference_option_name_from_key(key)
option = options_for_conference_select.find { |option| option[1] == key }
if option.present?
option.first
else
'Unknown conference option'
end
end
end

View File

@@ -2,6 +2,7 @@
<%= bootstrap_form_with model: model, local: true do |form| %>
<%= form.text_field :name %>
<%= form.select :conference_option, options_for_conference_select, { label: t('.labels.conference_option') }, class: "form-control custom-select" %>
<%= form.time_zone_select(:shoot_location_time_zone, nil, label: "Time zone of shoot location") %>
<div class="row align-items-center text-center mt-4">

View File

@@ -127,8 +127,8 @@
</div>
<hr>
<% end %>
<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' %>
<p class="card-text"><%= "If you want to join the #{conference_option_name_from_key(@broadcast.conference_option)} meeting dedicated to this broadcast, follow the link below." %></p>
<%= link_to 'Video Conference', @conference_url, class: "btn btn-primary btn-block", target: '_blank' %>
</div>
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
<div id="broadcast_recordings">

View File

@@ -17,6 +17,7 @@
<%= @user.role_for(Current.account).to_s.titleize %>
<% end %>
</p>
<%= link_to 'Auth to Microsoft', '/auth/azure_ad', method: :post, class: "btn btn-primary" %>
</div>
<div class="mt-3">

View File

@@ -0,0 +1,13 @@
require 'azure_ad'
Rails.application.config.middleware.use OmniAuth::Builder do
provider :azure_ad,
client_id: ENV['AZURE_CLIENT_ID'],
client_secret: ENV['AZURE_CLIENT_SECRET'],
redirect_uri: ENV['AZURE_REDIRECT_URI'],
client_options: {
token_url: "#{ENV['AZURE_TENANT_ID']}/oauth2/v2.0/token",
authorize_url: "#{ENV['AZURE_TENANT_ID']}/oauth2/v2.0/authorize"
},
scope: ENV['AZURE_SCOPES']
end

View File

@@ -275,6 +275,9 @@ en:
stream_multiple_cameras: Stream multiple cameras at one time
update:
reset_notice: The Share URL has been reset, and the previous URL will no longer work. Please click "Copy URL" and share it again with those who you want to have access to this live stream
form:
labels:
conference_option: Conference Option
bulk_taggings:
new_bulk_tag_modal:
submit: Add
@@ -1642,3 +1645,9 @@ en:
edit: Edit
report: Report
generating: Generating...
conference_meetings:
show:
alerts:
not_authenticated: You are not authenticated via Microsoft, please authenticate and try again
failed_to_join: Failed to join conference
unknown_conference_option: Unknown conference option

View File

@@ -131,6 +131,9 @@ es:
stream_multiple_cameras: Stream multiple cameras at one time
update:
reset_notice: The Share URL has been reset, and the previous URL will no longer work. Please click "Copy URL" and share it again with those who you want to have access to this live stream
form:
labels:
conference_option: Conference Option (ES)
contract_templates:
blank_contracts:
create:
@@ -705,3 +708,9 @@ es:
production_elements_logs: Production Elements Logs, and more (ES)
reduces_labor_cost: Reduces labor costs (ES)
simplifies_cue_sheets: Simplifies Music Cue Sheets, Graphic Cue Sheets (ES)
conference_meetings:
show:
alerts:
not_authenticated: You are not authenticated via Microsoft, please authenticate and try again (ES)
failed_to_join: Failed to join conference (ES)
unknown_conference_option: Unknown conference option (ES)

View File

@@ -4,6 +4,8 @@ require 'sidekiq/web'
Rails.application.routes.draw do
AVAILABLE_LOCALES_REGEX = /#{I18n.available_locales.join("|")}/.freeze
get 'auth/azure_ad/callback', to: 'callbacks#create'
concern :confirmable do
resources :video_release_confirmations, only: [:new, :create, :destroy]
end
@@ -100,7 +102,7 @@ Rails.application.routes.draw do
member do
delete :destroy_file
end
resource :zoom_meeting, only: [:show]
resource :conference_meeting, only: [:show]
resources :broadcast_recordings, only: :destroy
end
resources :directories, except: [:index] do

View File

@@ -0,0 +1,8 @@
class AddMicrosoftInfoToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :microsoft_user_id, :string
add_column :users, :microsoft_access_token, :string
add_column :users, :microsoft_refresh_token, :string
add_column :users, :microsoft_token_expires_at, :integer
end
end

View File

@@ -0,0 +1,6 @@
class AddConferenceDetailsToBroadcasts < ActiveRecord::Migration[6.0]
def change
add_column :broadcasts, :conference_option, :string
add_column :broadcasts, :conference_join_url, :string
end
end

View File

@@ -9,20 +9,6 @@ SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
--
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
--
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
--
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
--
-- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: -
--
@@ -600,7 +586,9 @@ CREATE TABLE public.broadcasts (
stream_key_override character varying,
director_mode_video_embed text,
simulcast_uid character varying,
video_conference_url_override character varying
video_conference_url_override character varying,
conference_option character varying,
conference_join_url character varying
);
@@ -1503,6 +1491,7 @@ CREATE TABLE public.settings (
--
CREATE SEQUENCE public.settings_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1538,6 +1527,7 @@ CREATE TABLE public.taggings (
--
CREATE SEQUENCE public.taggings_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1568,6 +1558,7 @@ CREATE TABLE public.tags (
--
CREATE SEQUENCE public.tags_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1815,7 +1806,11 @@ CREATE TABLE public.users (
remember_created_at timestamp without time zone,
first_name character varying,
last_name character varying,
time_zone character varying DEFAULT 'UTC'::character varying NOT NULL
time_zone character varying DEFAULT 'UTC'::character varying NOT NULL,
microsoft_user_id character varying,
microsoft_access_token character varying,
microsoft_refresh_token character varying,
microsoft_token_expires_at integer
);
@@ -4036,6 +4031,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200807190607'),
('20200811102720'),
('20200812060406'),
('20200819070738');
('20200819070738'),
('20200820081251'),
('20200820081524');

124
lib/azure_ad.rb Normal file
View File

@@ -0,0 +1,124 @@
require 'omniauth-oauth2'
# This file is from omniauth-microsoft_graph lib (not installed)
# It is modified to make auth work
module OmniAuth
module Strategies
class AzureAd < OmniAuth::Strategies::OAuth2
BASE_SCOPE_URL = 'https://graph.microsoft.com/'
BASE_SCOPES = %w[offline_access openid email profile].freeze
DEFAULT_SCOPE = 'offline_access openid email profile User.Read'.freeze
option :name, :azure_ad
option :client_options,
site: 'https://login.microsoftonline.com/'
option :authorize_options, %i[state callback_url scope response_mode]
option :token_params, {}
option :scope, DEFAULT_SCOPE
option :authorized_client_ids, []
uid { raw_info["id"] }
info do
{
# 'email' => raw_info["mail"],
# 'first_name' => raw_info["givenName"],
# 'last_name' => raw_info["surname"],
# 'name' => [raw_info["givenName"], raw_info["surname"]].join(' '),
# 'nickname' => raw_info["displayName"],
}
end
extra do
{
# 'raw_info' => raw_info,
# 'params' => access_token.params,
# 'aud' => options.client_id
}
end
def authorize_params
super.tap do |params|
options[:authorize_options].each do |k|
params[k] = request.params[k.to_s] unless [nil, ''].include?(request.params[k.to_s])
end
params[:scope] = get_scope(params)
session['omniauth.state'] = params[:state] if params[:state]
end
end
def raw_info
@raw_info ||= access_token.get('https://graph.microsoft.com/v1.0/me').parsed
end
def callback_url
options[:callback_url] || full_host + script_name + callback_path
end
def custom_build_access_token
token_response = get_access_token(request)
session[:microsoft_graph_api_token] = token_response.token
token_response
end
alias build_access_token custom_build_access_token
private
def get_access_token(request)
verifier = request.params['code']
redirect_uri = request.params['redirect_uri'] || request.params['callback_url']
if verifier && request.xhr?
client_get_token(verifier, redirect_uri || '/auth/azure_ad/callback')
elsif verifier
client_get_token(verifier, redirect_uri || callback_url)
elsif verify_token(request.params['access_token'])
::OAuth2::AccessToken.from_hash(client, request.params.dup)
elsif request.content_type =~ /json/i
begin
body = JSON.parse(request.body.read)
request.body.rewind # rewind request body for downstream middlewares
verifier = body && body['code']
client_get_token(verifier, '/auth/azure_ad/callback') if verifier
rescue JSON::ParserError => e
warn "[omniauth google-oauth2] JSON parse error=#{e}"
end
end
end
def client_get_token(verifier, redirect_uri)
client.auth_code.get_token(verifier, get_token_options(redirect_uri), get_token_params)
end
def get_token_params
deep_symbolize(options.auth_token_params || {})
end
def get_token_options(redirect_uri = '')
{ redirect_uri: redirect_uri }.merge(token_params.to_hash(symbolize_keys: true))
end
def get_scope(params)
raw_scope = params[:scope] || DEFAULT_SCOPE
scope_list = raw_scope.split(' ').map { |item| item.split(',') }.flatten
scope_list.map! { |s| s =~ %r{^https?://} || BASE_SCOPES.include?(s) ? s : "#{BASE_SCOPE_URL}#{s}" }
scope_list.join(' ')
end
def verify_token(access_token)
return false unless access_token
# access_token.get('https://graph.microsoft.com/v1.0/me').parsed
raw_response = client.request(:get, 'https://graph.microsoft.com/v1.0/me',
params: { access_token: access_token }).parsed
(raw_response['aud'] == options.client_id) || options.authorized_client_ids.include?(raw_response['aud'])
end
end
end
end

107
lib/microsoft_graph.rb Normal file
View File

@@ -0,0 +1,107 @@
require 'httparty'
class MicrosoftGraph
BASE_URL = 'https://graph.microsoft.com/v1.0'.freeze
def initialize(current_user, client_id, client_secret, tenant_id, scopes)
@current_user = current_user
@uid = current_user.microsoft_user_id
@token = current_user.microsoft_access_token
@refresh_token = current_user.microsoft_refresh_token
@token_expires_at = current_user.microsoft_token_expires_at
@client_id = client_id
@client_secret = client_secret
@tenant_id = tenant_id
@scopes = scopes
end
def create_teams_meeting(subject)
if @refresh_token.nil? || @token_expires_at.nil?
raise ActionController::InvalidAuthenticityToken, 'Missing refresh token / token expiration'
return
end
# 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
refresh_access_token
end
if @token.nil?
raise ActionController::InvalidAuthenticityToken, 'Missing access token'
return
end
response = HTTParty.post(
"#{BASE_URL}/me/onlineMeetings",
body: {
subject: subject,
participants: {
organizer: {
identity: {
user: {
id: @uid
}
}
}
}
}.to_json,
headers: {
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
Rails.logger.error('[Microsoft Graph Error]')
Rails.logger.error(response.inspect)
raise StandardError, "Failed to create teams meeting [#{response.code}]"
else
JSON.parse(response.body)
end
end
private
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: {
client_id: @client_id,
client_secret: @client_secret,
refresh_token: @refresh_token,
grant_type: 'refresh_token',
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

View File

@@ -157,16 +157,24 @@ RSpec.describe BroadcastsController, type: :controller do
expect(response.body).to have_xpath "//input[@readonly][@value='#{broadcast_url(broadcast.token)}']"
end
it "displays zoom meeting button" do
it "displays zoom meeting button for zoom conference option" do
get :show, params: { project_id: project.id, id: broadcast.id }
expect(response.body).to have_link("Video Conference", href: project_broadcast_zoom_meeting_url(project, broadcast))
expect(response.body).to have_link("Video Conference", href: project_broadcast_conference_meeting_url(project, broadcast))
end
it "displays microsoft teams meeting button for MS Teams conference option" do
ms_teams_broadcast = create(:broadcast, :ms_teams_conference, project: project )
get :show, params: { project_id: project.id, id: ms_teams_broadcast.id }
expect(response.body).to have_content 'MS Teams'
expect(response.body).to have_link 'Video Conference', href: project_broadcast_conference_meeting_url(project, ms_teams_broadcast)
end
it "assigns required variables" do
get :show, params: { project_id: project.id, id: broadcast.id }
expect(assigns(:conference_url)).to eq project_broadcast_zoom_meeting_url(project, broadcast)
expect(assigns(:conference_url)).to eq project_broadcast_conference_meeting_url(project, broadcast)
expect(assigns(:broadcast)).to eq broadcast
end

View File

@@ -0,0 +1,75 @@
require 'rails_helper'
RSpec.describe ConferenceMeetingsController, type: :controller do
let(:user) { create(:user) }
let(:account) { user.primary_account }
let(:project) { create(:project, account: user.primary_account) }
let(:broadcast) { create(:broadcast, name: "Broadcast", project: project) }
let(:ms_teams_broadcast) { create(:broadcast, :ms_teams_conference, project: project) }
let(:unknown_option_broadcast) { create(:broadcast, project: project, conference_option: 'google') }
let(:meeting_start_url) { "http://meeting_start_url" }
let(:meeting_hash) { HashWithIndifferentAccess.new(start_url: meeting_start_url) }
let(:user_create_response) { {"id" => "new_host_id"} }
let(:roles_assign_response) { {"ids" => ["new_host_id"]} }
let(:roles_list_response) { {"roles" => [{"name" => "directme-host"}]} }
before :each do
allow_any_instance_of(ZoomGateway).to receive(:find_meeting).and_return(meeting_hash)
allow_any_instance_of(ZoomGateway).to receive(:create_meeting).and_return("meeting_id")
allow_any_instance_of(ZoomGateway).to receive(:create_host).and_return("host_id")
stub_mux_live_stream
end
describe "#show" do
before { sign_in user }
it "redirects to meeting start url with Zoom conference option" do
get :show, params: { project_id: project.id, broadcast_id: broadcast.id }
expect(response).to redirect_to(meeting_start_url)
end
it "redirects to the broadcast show page with alert if user is not authenticated via microsoft and tries to create MS Teams meeting" do
get :show, params: { project_id: project.id, broadcast_id: ms_teams_broadcast.id }
expect(response).to redirect_to project_broadcast_path(project, ms_teams_broadcast)
expect(flash.alert).to eq not_authenticated_alert
end
it "redirects to the broadcast show page with alert if user is authenticated via microsoft and tries to create MS Teams meeting but Graph API fails to create meeting" do
allow_any_instance_of(MicrosoftGraph).to receive(:create_teams_meeting).and_return(nil)
get :show, params: { project_id: project.id, broadcast_id: ms_teams_broadcast.id }
expect(response).to redirect_to project_broadcast_path(project, ms_teams_broadcast)
expect(flash.alert).to eq failed_to_join_alert
end
it "redirects to the broadcast show page with alert if conference option is not reckognized" do
get :show, params: { project_id: project.id, broadcast_id: unknown_option_broadcast.id }
expect(response).to redirect_to project_broadcast_path(project, unknown_option_broadcast)
expect(flash.alert).to eq unknown_conference_option_alert
end
it "redirects to meeting start url with MS Teams conference option" do
new_ms_teams_meeting = JSON.parse({ joinUrl: meeting_start_url }.to_json)
allow_any_instance_of(MicrosoftGraph).to receive(:create_teams_meeting).and_return(new_ms_teams_meeting)
get :show, params: { project_id: project.id, broadcast_id: ms_teams_broadcast.id }
expect(response).to redirect_to(meeting_start_url)
end
end
private
def not_authenticated_alert
t 'conference_meetings.show.alerts.not_authenticated'
end
def failed_to_join_alert
t 'conference_meetings.show.alerts.failed_to_join'
end
def unknown_conference_option_alert
t 'conference_meetings.show.alerts.unknown_conference_option'
end
end

View File

@@ -1,30 +0,0 @@
require 'rails_helper'
RSpec.describe ZoomMeetingsController, type: :controller do
let(:user) { create(:user) }
let(:account) { user.primary_account }
let(:project) { create(:project, account: user.primary_account) }
let(:broadcast) { create(:broadcast, name: "Broadcast", project: project) }
let(:meeting_start_url) { "http://meeting_start_url" }
let(:meeting_hash) { HashWithIndifferentAccess.new(start_url: meeting_start_url) }
let(:user_create_response) { {"id" => "new_host_id"} }
let(:roles_assign_response) { {"ids" => ["new_host_id"]} }
let(:roles_list_response) { {"roles" => [{"name" => "directme-host"}]} }
before :each do
allow_any_instance_of(ZoomGateway).to receive(:find_meeting).and_return(meeting_hash)
allow_any_instance_of(ZoomGateway).to receive(:create_meeting).and_return("meeting_id")
allow_any_instance_of(ZoomGateway).to receive(:create_host).and_return("host_id")
stub_mux_live_stream
end
describe "#show" do
before { sign_in user }
it "redirects to meeting start url" do
get :show, params: { project_id: project.id, broadcast_id: broadcast.id }
expect(response).to redirect_to(meeting_start_url)
end
end
end

View File

@@ -2,11 +2,16 @@ FactoryBot.define do
factory :broadcast do
association :project
name "My Live Stream"
conference_option "zoom"
transient do
skip_create_callback false
end
trait :ms_teams_conference do
conference_option "ms_teams"
end
trait :with_stream do
stream_uid "mux_stream"
stream_key "mux_key"

View File

@@ -25,6 +25,7 @@ feature 'User managing broadcasts' do
by 'filling out the form' do
fill_in broadcast_name_field, with: 'My Broadcast'
select_conference_option('Zoom')
select_time_zone("New Delhi")
end
@@ -352,5 +353,10 @@ feature 'User managing broadcasts' do
t 'broadcasts.file.actions.delete_file'
end
def select_conference_option(value)
if value.present?
select value, from: "broadcast[conference_option]"
end
end
end