Skip to content
Snippets Groups Projects
Unverified Commit 61c460f9 authored by Ahmad Farhat's avatar Ahmad Farhat Committed by GitHub
Browse files

Refactor meeting start (#3479)

parent c5a55492
No related branches found
No related tags found
No related merge requests found
......@@ -29,25 +29,11 @@ module Api
# Returns: { data: Array[serializable objects] , errors: Array[String] }
# Does: Starts the Room meeting and joins in the meeting starter.
def start
# TODO: amir - Check the legitimately of the action.
bbb_api = BigBlueButtonApi.new
meeting_starter = current_user ? "user(id):#{current_user.id}" : 'unauthenticated user'
options = { logoutURL: request.headers['Referer'] || root_url }
retries = 0
begin
logger.info "Starting meeting for room(friendly_id):#{@room.friendly_id} by #{meeting_starter}."
join_url = bbb_api.start_meeting room: @room, meeting_starter: current_user, options: options
logger.info "meeting successfully started for room(friendly_id):#{@room.friendly_id} by #{meeting_starter}."
MeetingStarter.new(room: @room, logout_url: request.referer).call
ActionCable.server.broadcast "#{@room.friendly_id}_rooms_channel", 'started'
render_json data: { join_url: }, status: :created
rescue BigBlueButton::BigBlueButtonException => e
retries += 1
logger.info "Retrying meeting start for room(friendly_id):#{@room.friendly_id} because of error(key): #{e.key} #{retries} times..."
retry unless retries >= 3
raise e
end
render_json data: {
join_url: BigBlueButtonApi.new.join_meeting(room: @room, name: current_user.name, role: 'Moderator')
}, status: :created
end
# POST /api/v1/rooms.json
......@@ -69,7 +55,7 @@ module Api
# GET /api/v1/rooms/:friendly_id/join.json
def join
render_json(data: BigBlueButtonApi.new.join_meeting(room: @room, name: params[:name]), status: :ok)
render_json(data: BigBlueButtonApi.new.join_meeting(room: @room, name: params[:name], role: 'Viewer'), status: :ok)
end
# GET /api/v1/rooms/:friendly_id/status.json
......@@ -77,7 +63,7 @@ module Api
data = {
status: BigBlueButtonApi.new.meeting_running?(room: @room)
}
data[:joinUrl] = BigBlueButtonApi.new.join_meeting(room: @room, name: params[:name]) if data[:status]
data[:joinUrl] = BigBlueButtonApi.new.join_meeting(room: @room, name: params[:name], role: 'Viewer') if data[:status]
render_json(data:, status: :ok)
end
......
......@@ -4,4 +4,12 @@ class MeetingOption < ApplicationRecord
has_many :room_meeting_options, dependent: :restrict_with_exception
validates :name, presence: true, uniqueness: true
def self.bbb_options(room_id:)
MeetingOption
.joins(:room_meeting_options)
.where.not('name LIKE :prefix', prefix: 'gl%') # ignore gl settings
.where(room_meeting_options: { room_id: })
.pluck(:name, :value)
end
end
......@@ -13,23 +13,16 @@ class BigBlueButtonApi
end
# Start a meeting for a specific room and returns the join URL.
def start_meeting(room:, meeting_starter:, options: {})
create_options = default_create_opts.merge(options)
join_options = { join_via_html5: true } # TODO: amir - Revisit this (createTime,...).
user_name = meeting_starter&.name || 'Someone'
password = (meeting_starter && create_options[:moderatorPW]) || create_options[:attendeePW]
bbb_server.create_meeting room.name, room.friendly_id, create_options
bbb_server.join_meeting_url room.friendly_id, user_name, password, join_options
def start_meeting(room:, options: {})
bbb_server.create_meeting room.name, room.meeting_id, options
end
def join_meeting(room:, name:)
bbb_server.join_meeting_url room.friendly_id, name, '', { role: 'Viewer' }
def join_meeting(room:, name:, role:)
bbb_server.join_meeting_url room.meeting_id, name, '', { role: }
end
def meeting_running?(room:)
bbb_server.is_meeting_running?(room.friendly_id)
bbb_server.is_meeting_running?(room.meeting_id)
end
# Retrieve the recordings that belong to room with given meeting_id
......@@ -51,20 +44,4 @@ class BigBlueButtonApi
def bbb_secret
ENV.fetch 'BIGBLUEBUTTON_SECRET', '8cd8ef52e8e101574e400365b55e11a6'
end
def default_create_opts
{
# TODO: amir - revisit this.
record: true,
logoutURL: 'http://localhost',
moderatorPW: 'mp',
attendeePW: 'ap',
moderatorOnlyMessage: 'Welcome Moderator',
muteOnStart: false,
guestPolicy: 'ALWAYS_ACCEPT',
'meta_gl-v3-listed': 'public',
'meta_bbb-origin-version': 3,
'meta_bbb-origin': 'Greenlight'
}
end
end
# frozen_string_literal: true
class MeetingStarter
def initialize(room:, logout_url:)
@room = room
@logout_url = logout_url
end
def call
# TODO: amir - Check the legitimately of the action.
options = MeetingOption.bbb_options(room_id: @room.id).to_h
options.merge!(computed_options)
retries = 0
begin
BigBlueButtonApi.new.start_meeting room: @room, options: options
ActionCable.server.broadcast "#{@room.friendly_id}_rooms_channel", 'started'
rescue BigBlueButton::BigBlueButtonException => e
retries += 1
retry unless retries >= 3
raise e
end
end
private
def computed_options
{
logoutURL: @logout_url,
'meta_bbb-origin-version': 3,
'meta_bbb-origin': 'greenlight'
}
end
end
......@@ -12,14 +12,6 @@ class PopulateMeetingOptions < ActiveRecord::Migration[7.0]
{ name: 'record', default_value: 'false' }, # true | false
{ name: 'muteOnStart', default_value: 'false' }, # true | false
{ name: 'guestPolicy', default_value: 'ALWAYS_ACCEPT' }, # ALWAYS_ACCEPT | ALWAYS_DENY | ASK_MODERATOR
{ name: 'logoutURL', default_value: '' },
{ name: 'logo', default_value: 'https://blindsidenetworks.com/wp-content/uploads/2021/04/cropped-bn_logo-02.png' },
{ name: 'attendeePW', default_value: '' },
{ name: 'moderatorPW', default_value: '' },
# Meta parameters:
{ name: 'meta_gl-v3-listed', default_value: 'public' },
{ name: 'meta_bbb-origin-version', default_value: 3 },
{ name: 'meta_bbb-origin', default_value: 'Greenlight' },
# GL only options:
{ name: 'glAnyoneCanStart', default_value: 'false' }, # true | false
{ name: 'glAnyoneJoinAsModerator', default_value: 'false' }, # true | false
......
......@@ -55,25 +55,41 @@ RSpec.describe Api::V1::RoomsController, type: :controller do
end
end
describe '#start_meeting' do
let(:join_url) { 'https://test.com/bigbluebutton/api?join' }
let(:bbb_service) { instance_double(BigBlueButtonApi) }
describe '#start' do
let(:room) { create(:room, user:) }
before do
allow(BigBlueButtonApi).to receive(:new).and_return(bbb_service)
allow(bbb_service).to receive(:start_meeting).and_return(join_url)
allow_any_instance_of(BigBlueButtonApi)
.to receive(:start_meeting)
.and_return(true)
end
it 'returns the join_url for existent room' do
room = create(:room, user:)
it 'makes a call to the MeetingStarter service with the right values' do
logout = 'http://example.com'
request.env['HTTP_REFERER'] = logout
expect(MeetingStarter).to receive(:new).with(room:, logout_url: logout)
post :start, params: { friendly_id: room.friendly_id }
expect(response).to have_http_status(:created)
expect(JSON.parse(response.body)['data']['join_url']).to eq(join_url)
end
it 'returns :not_found if the room doesn\'t exist' do
post :start, params: { friendly_id: 'invalid_friendly_id' }
expect(response).to have_http_status(:not_found)
it 'makes a call to the BigBlueButtonApi to get the join url' do
expect_any_instance_of(BigBlueButtonApi)
.to receive(:join_meeting)
.with(room:, name: user.name, role: 'Moderator')
post :start, params: { friendly_id: room.friendly_id }
end
it 'returns the join url to the front-end for redirecting' do
allow_any_instance_of(BigBlueButtonApi)
.to receive(:join_meeting)
.and_return('https://example.com')
post :start, params: { friendly_id: room.friendly_id }
expect(response).to have_http_status(:created)
expect(JSON.parse(response.body)['data']['join_url']).to eq('https://example.com')
end
end
......@@ -116,7 +132,7 @@ RSpec.describe Api::V1::RoomsController, type: :controller do
it 'calls the BigBlueButton service with the right values' do
room = create(:room, user:)
expect_any_instance_of(BigBlueButtonApi).to receive(:join_meeting).with(room:, name: user.name)
expect_any_instance_of(BigBlueButtonApi).to receive(:join_meeting).with(room:, name: user.name, role: 'Viewer')
get :join, params: { friendly_id: room.friendly_id, name: user.name }
end
end
......@@ -134,7 +150,7 @@ RSpec.describe Api::V1::RoomsController, type: :controller do
room = create(:room, user:)
allow_any_instance_of(BigBlueButtonApi).to receive(:meeting_running?).and_return(true)
expect_any_instance_of(BigBlueButtonApi).to receive(:join_meeting).with(room:, name: user.name)
expect_any_instance_of(BigBlueButtonApi).to receive(:join_meeting).with(room:, name: user.name, role: 'Viewer')
get :status, params: { friendly_id: room.friendly_id, name: user.name }
end
......
# frozen_string_literal: true
FactoryBot.define do
factory :room_meeting_option do
room
meeting_option
value { [true, false, '12345'].sample }
end
end
......@@ -8,4 +8,26 @@ RSpec.describe MeetingOption, type: :model do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name) }
end
describe '#bbb_options' do
it 'returns all non-greenlight options' do
room = create(:room)
setting1 = create(:meeting_option, name: 'glSetting1')
setting2 = create(:meeting_option, name: 'glSetting2')
setting3 = create(:meeting_option, name: 'setting1')
setting4 = create(:meeting_option, name: 'setting2')
create(:room_meeting_option, room:, meeting_option: setting1)
create(:room_meeting_option, room:, meeting_option: setting2)
room_option1 = create(:room_meeting_option, room:, meeting_option: setting3, value: 'value1')
room_option2 = create(:room_meeting_option, room:, meeting_option: setting4, value: 'value2')
expect(
described_class.bbb_options(room_id: room.id)
).to eq([
[setting3.name, room_option1.value],
[setting4.name, room_option2.value]
])
end
end
end
......@@ -5,18 +5,6 @@ require 'bigbluebutton_api'
describe BigBlueButtonApi, type: :service do
let(:bbb_service) { described_class.new }
let(:default_create_opts) do
{
moderatorPW: 'mp',
attendeePW: 'ap'
}
end
let(:default_join_opts) do
{
join_via_html5: true
}
end
before do
ENV['BIGBLUEBUTTON_ENDPOINT'] = 'http://test.com/bigbluebutton/api'
......@@ -38,42 +26,4 @@ describe BigBlueButtonApi, type: :service do
expect(bbb_api).to eq(bbb_api2).and eq(bbb_api3)
end
end
describe 'Room meeting creation' do
let(:bbb_server) { instance_double(BigBlueButton::BigBlueButtonApi) }
let(:room) { create(:room) }
let(:meeting_starter) { room.user }
before do
allow(bbb_service).to receive(:default_create_opts).and_return(default_create_opts)
allow(BigBlueButton::BigBlueButtonApi).to receive(:new).and_return(bbb_server)
allow(bbb_server).to receive(:create_meeting).and_return(true)
allow(bbb_server).to receive(:join_meeting_url).and_return(true)
end
it 'calls bbb_api#create_meeting' do
expect(bbb_server).to receive(:create_meeting).with(room.name, room.friendly_id, default_create_opts)
bbb_service.start_meeting room:, meeting_starter: nil, options: {}
end
describe 'calls bbb_api#join_meeting_url' do
it 'With Moderator password and the meeting starter name for authenticated requests' do
expect(bbb_server).to receive(:join_meeting_url).with(room.friendly_id, meeting_starter.name, default_create_opts[:moderatorPW],
default_join_opts)
bbb_service.start_meeting room:, meeting_starter:, options: {}
end
it 'With attendee password and user name as "Someone" for unauthenticated requests' do
expect(bbb_server).to receive(:join_meeting_url).with(room.friendly_id, 'Someone', default_create_opts[:attendeePW], default_join_opts)
bbb_service.start_meeting room:, meeting_starter: nil, options: {}
end
end
describe 'calls bbb#get_recordings' do
it 'With list of meeting ids given as param' do
expect(bbb_server).to receive(:get_recordings).with(meetingID: [1, 2, 3])
bbb_service.get_recordings(meeting_ids: [1, 2, 3])
end
end
end
end
# frozen_string_literal: true
require 'rails_helper'
describe MeetingStarter, type: :service do
let(:room) { create(:room) }
let(:service) { described_class.new(room:, logout_url: 'http://example.com') }
let(:options) do
{
logoutURL: 'http://example.com',
'meta_bbb-origin-version': 3,
'meta_bbb-origin': 'greenlight'
}
end
describe '#call' do
it 'calls BigBlueButtonApi with the right params' do
expect_any_instance_of(BigBlueButtonApi)
.to receive(:start_meeting)
.with(room:, options:)
service.call
end
it 'merges the options with the computed options' do
allow_any_instance_of(described_class)
.to receive(:computed_options)
.and_return({ test: 'test' })
expect_any_instance_of(BigBlueButtonApi)
.to receive(:start_meeting)
.with(room:, options: { test: 'test' })
service.call
end
it 'retries 3 times if the call fails' do
allow(BigBlueButtonApi)
.to receive(:new)
.and_raise(BigBlueButton::BigBlueButtonException)
expect(BigBlueButtonApi)
.to receive(:new)
.exactly(3).times
expect { service.call }.to raise_error(BigBlueButton::BigBlueButtonException)
end
it 'broadcasts to ActionCable that the meeting has started' do
allow_any_instance_of(BigBlueButtonApi)
.to receive(:start_meeting)
.and_return(true)
expect(ActionCable.server)
.to receive(:broadcast)
.with("#{room.friendly_id}_rooms_channel", 'started')
service.call
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment