bigbluebutton_rails 2.2.0 → 2.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +34 -0
- data/Dockerfile +23 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +14 -5
- data/README.md +42 -59
- data/Rakefile +6 -3
- data/app/controllers/bigbluebutton/api/rooms_controller.rb +117 -0
- data/app/controllers/bigbluebutton/playback_types_controller.rb +0 -3
- data/app/controllers/bigbluebutton/recordings_controller.rb +37 -23
- data/app/controllers/bigbluebutton/rooms_controller.rb +6 -31
- data/app/controllers/bigbluebutton/servers_controller.rb +0 -14
- data/app/helpers/bigbluebutton_rails_helper.rb +4 -0
- data/app/models/bigbluebutton_attendee.rb +21 -0
- data/app/models/bigbluebutton_meeting.rb +5 -0
- data/app/models/bigbluebutton_playback_format.rb +3 -2
- data/app/models/bigbluebutton_recording.rb +164 -68
- data/app/models/bigbluebutton_room.rb +148 -59
- data/app/models/bigbluebutton_server.rb +1 -1
- data/app/views/bigbluebutton/api/error.rabl +10 -0
- data/app/views/bigbluebutton/api/rooms/index.rabl +45 -0
- data/app/views/bigbluebutton/api/rooms/join.rabl +6 -0
- data/app/views/bigbluebutton/api/rooms/running.rabl +10 -0
- data/app/views/bigbluebutton/recordings/play.html.erb +1 -0
- data/app/views/bigbluebutton/servers/_activity_list.html.erb +5 -5
- data/app/workers/{bigbluebutton_finish_meetings.rb → bigbluebutton_finish_meetings_worker.rb} +2 -2
- data/app/workers/{bigbluebutton_meeting_updater.rb → bigbluebutton_meeting_updater_worker.rb} +3 -3
- data/app/workers/bigbluebutton_recordings_for_room_worker.rb +27 -0
- data/app/workers/{bigbluebutton_update_recordings.rb → bigbluebutton_update_recordings_worker.rb} +2 -2
- data/app/workers/{bigbluebutton_update_server_configs.rb → bigbluebutton_update_server_configs_worker.rb} +3 -3
- data/bigbluebutton_rails.gemspec +1 -0
- data/config/locales/en.yml +21 -0
- data/config/locales/pt-br.yml +1 -0
- data/config/resque/resque.rake +7 -1
- data/config/resque/workers_schedule.yml +3 -3
- data/docker-compose.yml +68 -0
- data/dumb-init_1.2.0 +0 -0
- data/lib/bigbluebutton_rails.rb +1 -2
- data/lib/bigbluebutton_rails/api_controller_methods.rb +138 -0
- data/lib/bigbluebutton_rails/configuration.rb +33 -4
- data/lib/bigbluebutton_rails/controller_methods.rb +0 -31
- data/lib/bigbluebutton_rails/exceptions.rb +17 -0
- data/lib/bigbluebutton_rails/rails/routes.rb +14 -0
- data/lib/bigbluebutton_rails/version.rb +1 -1
- data/lib/generators/bigbluebutton_rails/templates/migration.rb +17 -3
- data/lib/generators/bigbluebutton_rails/templates/migration_2_2_0.rb +1 -1
- data/lib/generators/bigbluebutton_rails/templates/migration_2_2_0_b.rb +106 -0
- data/lib/generators/bigbluebutton_rails/templates/migration_2_3_0.rb +22 -0
- data/lib/tasks/bigbluebutton_rails/meetings.rake +1 -1
- data/spec/controllers/bigbluebutton/api/rooms_controller_spec.rb +498 -0
- data/spec/controllers/bigbluebutton/recordings_controller_spec.rb +84 -0
- data/spec/controllers/bigbluebutton/rooms_controller_exception_handling_spec.rb +2 -2
- data/spec/controllers/bigbluebutton/rooms_controller_spec.rb +24 -22
- data/spec/controllers/bigbluebutton/servers_controller_spec.rb +9 -0
- data/spec/factories/bigbluebutton_attendee.rb +7 -0
- data/spec/factories/bigbluebutton_meeting.rb +0 -1
- data/spec/factories/bigbluebutton_playback_type.rb +1 -0
- data/spec/factories/bigbluebutton_recording.rb +2 -4
- data/spec/helpers/bigbluebutton_rails_helper_spec.rb +10 -2
- data/spec/lib/bigbluebutton_rails/background_tasks_spec.rb +1 -0
- data/spec/lib/tasks/meetings_rake_spec.rb +1 -1
- data/spec/models/bigbluebutton_attendee_spec.rb +44 -0
- data/spec/models/bigbluebutton_meeting_db_spec.rb +0 -1
- data/spec/models/bigbluebutton_meeting_spec.rb +1 -0
- data/spec/models/bigbluebutton_playback_format_spec.rb +2 -0
- data/spec/models/bigbluebutton_playback_type_db_spec.rb +1 -0
- data/spec/models/bigbluebutton_recording_db_spec.rb +2 -2
- data/spec/models/bigbluebutton_recording_spec.rb +404 -72
- data/spec/models/bigbluebutton_room_spec.rb +435 -148
- data/spec/rails_app/config/database.yml.example +10 -11
- data/spec/rails_app/config/initializers/resque.rb +33 -0
- data/spec/rails_app/config/routes.rb +2 -0
- data/spec/rails_app/lib/tasks/db/populate.rake +73 -55
- data/spec/support/mocked_server.rb +1 -1
- data/spec/workers/{bigbluebutton_finish_meetings_spec.rb → bigbluebutton_finish_meetings_worker_spec.rb} +3 -3
- data/spec/workers/{bigbluebutton_meeting_updater_spec.rb → bigbluebutton_meeting_updater_worker_spec.rb} +8 -7
- data/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb +49 -0
- data/spec/workers/{bigbluebutton_update_recordings_spec.rb → bigbluebutton_update_recordings_worker_spec.rb} +3 -3
- data/spec/workers/{bigbluebutton_update_server_configs_spec.rb → bigbluebutton_update_server_configs_worker_spec.rb} +4 -4
- metadata +42 -14
- data/lib/classes/bigbluebutton_attendee.rb +0 -21
- data/spec/classes/bigbluebutton_attendee_spec.rb +0 -76
- data/spec/controllers/bigbluebutton/recordings_controller_json_responses_spec.rb +0 -111
- data/spec/controllers/bigbluebutton/rooms_controller_json_responses_spec.rb +0 -203
- data/spec/controllers/bigbluebutton/servers_controller_json_responses_spec.rb +0 -162
|
@@ -11,12 +11,17 @@ module BigbluebuttonRails
|
|
|
11
11
|
attr_accessor :user_attr_name
|
|
12
12
|
attr_accessor :user_attr_id
|
|
13
13
|
attr_accessor :use_local_voice_bridges
|
|
14
|
+
attr_accessor :api_secret
|
|
15
|
+
attr_accessor :playback_url_authentication
|
|
16
|
+
attr_accessor :playback_iframe
|
|
17
|
+
attr_accessor :downloadable_playback_types
|
|
14
18
|
|
|
15
19
|
# methods
|
|
16
20
|
attr_accessor :select_server
|
|
17
21
|
attr_accessor :match_room_recording
|
|
18
22
|
attr_accessor :get_invitation_url
|
|
19
|
-
attr_accessor :
|
|
23
|
+
attr_accessor :get_create_options
|
|
24
|
+
attr_accessor :get_join_options
|
|
20
25
|
|
|
21
26
|
def initialize
|
|
22
27
|
@controllers = {
|
|
@@ -40,6 +45,17 @@ module BigbluebuttonRails
|
|
|
40
45
|
@use_local_voice_bridges = false
|
|
41
46
|
@guest_support = false
|
|
42
47
|
|
|
48
|
+
# Flag to use authentication on playback url
|
|
49
|
+
@playback_url_authentication = false
|
|
50
|
+
|
|
51
|
+
# If true, show all non downloadable playbacks inside an iframe instead of
|
|
52
|
+
# redirecting to their URL
|
|
53
|
+
@playback_iframe = false
|
|
54
|
+
|
|
55
|
+
# Name of the playback formats that can be downloaded. They are treated differently
|
|
56
|
+
# from those that play in a web page, for example.
|
|
57
|
+
@downloadable_playback_types = ['presentation_video', 'presentation_export']
|
|
58
|
+
|
|
43
59
|
# How to find the room of a recording using the `data` returned by
|
|
44
60
|
# a `getRecordings`.
|
|
45
61
|
@match_room_recording = Proc.new do |data, *args|
|
|
@@ -50,9 +66,13 @@ module BigbluebuttonRails
|
|
|
50
66
|
# returns nil (disable the feature).
|
|
51
67
|
@get_invitation_url = Proc.new{ |room| nil }
|
|
52
68
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
@
|
|
69
|
+
# Define this method to return extra parameters to be passed to a `create` call.
|
|
70
|
+
# `user` is the user creating the meeting, if any.
|
|
71
|
+
@get_create_options = Proc.new{ |room, user| nil }
|
|
72
|
+
|
|
73
|
+
# Define this method to return extra parameters to be passed to a `join` call.
|
|
74
|
+
# `user` is the user joining the meeting, if any.
|
|
75
|
+
@get_join_options = Proc.new{ |room, user| nil }
|
|
56
76
|
|
|
57
77
|
# Selects a server to be used by `room` whenever it needs to make API calls.
|
|
58
78
|
# By default, if no servers are available an exception is raised.
|
|
@@ -68,6 +88,15 @@ module BigbluebuttonRails
|
|
|
68
88
|
@select_server = Proc.new do |room, api_method=nil|
|
|
69
89
|
room.select_server(api_method)
|
|
70
90
|
end
|
|
91
|
+
|
|
92
|
+
# Secret used to authenticate API calls. API calls received are expected to set
|
|
93
|
+
# the HTTP header "Authorization" with a value "Bearer 123123123123", where
|
|
94
|
+
# "123123123123" is the secret set in `api_secret`.
|
|
95
|
+
# Notice that this header has the secret in clear text in the HTTP request, so
|
|
96
|
+
# only use it if your application is hosted with HTTPS.
|
|
97
|
+
# Set it to an empty string to disable authentication. Set it to nil to deny all
|
|
98
|
+
# requests (disable the API).
|
|
99
|
+
@api_secret = nil
|
|
71
100
|
end
|
|
72
101
|
|
|
73
102
|
def set_controllers(options)
|
|
@@ -106,37 +106,6 @@ module BigbluebuttonRails
|
|
|
106
106
|
role == :moderator
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
-
# Method called right before a meeting is created to get options that should receive
|
|
110
|
-
# priority over the options saved in the database when sending the <tt>create</tt>
|
|
111
|
-
# API call.
|
|
112
|
-
# The parameter 'room' is the BigbluebuttonRoom where the meeting is about
|
|
113
|
-
# to be created.
|
|
114
|
-
#
|
|
115
|
-
# By default, all options sent in a 'create' API call are taken from the
|
|
116
|
-
# options saved in the database for the target room. This includes, for example,
|
|
117
|
-
# the meeting's name, dial number, welcome message, if it should be recorded or not.
|
|
118
|
-
# This method can return a hash of options to force some of these options to be
|
|
119
|
-
# different from what is stored in the database. For example, if a room should *not* be
|
|
120
|
-
# recorded in case unprivileged users create it, this method can return <tt>{ record: false }</tt>
|
|
121
|
-
# to force the <tt>record</tt> flag to be false.
|
|
122
|
-
# The keys used in this hash should correspond to the attributes sent in the <tt>CREATE</tt>
|
|
123
|
-
# API call, not the the attributes stored in the database! See the method
|
|
124
|
-
# <tt>create_meeting</tt> in the gem <tt>bigbluebutton-api-ruby</tt> for examples.
|
|
125
|
-
# (https://github.com/mconf/bigbluebutton-api-ruby/blob/master/lib/bigbluebutton_api.rb#L156)
|
|
126
|
-
#
|
|
127
|
-
# You may want to override this in your ApplicationController to implement your own
|
|
128
|
-
# own logic, if needed. For example:
|
|
129
|
-
#
|
|
130
|
-
# def bigbluebutton_create_options(room)
|
|
131
|
-
# can_record = room.record && bigbluebutton_user.can_record?
|
|
132
|
-
# { record: can_record }
|
|
133
|
-
# end
|
|
134
|
-
# end
|
|
135
|
-
#
|
|
136
|
-
def bigbluebutton_create_options(room)
|
|
137
|
-
{}
|
|
138
|
-
end
|
|
139
|
-
|
|
140
109
|
end
|
|
141
110
|
end
|
|
142
111
|
|
|
@@ -7,4 +7,21 @@ module BigbluebuttonRails
|
|
|
7
7
|
# room that does not have a server associated
|
|
8
8
|
class ServerRequired < StandardError; end
|
|
9
9
|
|
|
10
|
+
# To help create responses for API errors
|
|
11
|
+
class APIError < StandardError
|
|
12
|
+
def initialize(msg, code=500, title=nil)
|
|
13
|
+
@title = title || msg
|
|
14
|
+
@code = code
|
|
15
|
+
super(msg)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def code
|
|
19
|
+
@code
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def title
|
|
23
|
+
@title
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
10
27
|
end
|
|
@@ -79,6 +79,20 @@ module ActionDispatch::Routing
|
|
|
79
79
|
send("bigbluebutton_routes_#{params[0].to_s}", options)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
def bigbluebutton_api_routes(api_scope='/api/conference')
|
|
83
|
+
scope api_scope do
|
|
84
|
+
resources :rooms, only: [] do
|
|
85
|
+
collection do
|
|
86
|
+
get :index, to: 'bigbluebutton/api/rooms#index'
|
|
87
|
+
end
|
|
88
|
+
member do
|
|
89
|
+
get :running, to: 'bigbluebutton/api/rooms#running'
|
|
90
|
+
post :join, to: 'bigbluebutton/api/rooms#join'
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
82
96
|
protected
|
|
83
97
|
|
|
84
98
|
def bigbluebutton_routes_default(*params) #:nodoc:
|
|
@@ -56,11 +56,12 @@ class CreateBigbluebuttonRails < ActiveRecord::Migration
|
|
|
56
56
|
t.string :meetingid
|
|
57
57
|
t.string :name
|
|
58
58
|
t.boolean :published, :default => false
|
|
59
|
-
t.
|
|
60
|
-
t.
|
|
59
|
+
t.decimal :start_time, precision: 14, scale: 0
|
|
60
|
+
t.decimal :end_time, precision: 14, scale: 0
|
|
61
61
|
t.boolean :available, :default => true
|
|
62
62
|
t.string :description
|
|
63
63
|
t.integer :size, limit: 8, default: 0
|
|
64
|
+
t.text :recording_users
|
|
64
65
|
t.timestamps
|
|
65
66
|
end
|
|
66
67
|
add_index :bigbluebutton_recordings, :room_id
|
|
@@ -86,6 +87,7 @@ class CreateBigbluebuttonRails < ActiveRecord::Migration
|
|
|
86
87
|
t.string :identifier
|
|
87
88
|
t.boolean :visible, :default => false
|
|
88
89
|
t.boolean :default, :default => false
|
|
90
|
+
t.boolean :downloadable, :default => false
|
|
89
91
|
t.timestamps
|
|
90
92
|
end
|
|
91
93
|
|
|
@@ -95,13 +97,14 @@ class CreateBigbluebuttonRails < ActiveRecord::Migration
|
|
|
95
97
|
t.integer :room_id
|
|
96
98
|
t.string :meetingid
|
|
97
99
|
t.string :name
|
|
98
|
-
t.datetime :start_time
|
|
99
100
|
t.decimal :create_time, precision: 14, scale: 0
|
|
101
|
+
t.decimal :finish_time, precision: 14, scale: 0
|
|
100
102
|
t.boolean :running, :default => false
|
|
101
103
|
t.boolean :recorded, :default => false
|
|
102
104
|
t.integer :creator_id
|
|
103
105
|
t.string :creator_name
|
|
104
106
|
t.boolean :ended, :default => false
|
|
107
|
+
t.string :got_stats
|
|
105
108
|
t.timestamps
|
|
106
109
|
end
|
|
107
110
|
add_index :bigbluebutton_meetings, [:meetingid, :create_time], :unique => true
|
|
@@ -111,6 +114,17 @@ class CreateBigbluebuttonRails < ActiveRecord::Migration
|
|
|
111
114
|
t.text :available_layouts
|
|
112
115
|
t.timestamps
|
|
113
116
|
end
|
|
117
|
+
|
|
118
|
+
create_table :bigbluebutton_attendees do |t|
|
|
119
|
+
t.string :user_id
|
|
120
|
+
t.string :external_user_id
|
|
121
|
+
t.string :user_name
|
|
122
|
+
t.decimal :join_time, precision: 14, scale: 0
|
|
123
|
+
t.decimal :left_time, precision: 14, scale: 0
|
|
124
|
+
t.integer :bigbluebutton_meeting_id
|
|
125
|
+
t.timestamps
|
|
126
|
+
end
|
|
127
|
+
|
|
114
128
|
end
|
|
115
129
|
|
|
116
130
|
def self.down
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class BigbluebuttonRailsTo220 < ActiveRecord::Migration
|
|
2
2
|
def self.up
|
|
3
3
|
remove_column :bigbluebutton_meetings, :server_id
|
|
4
|
-
remove_column :bigbluebutton_rooms, :server_id
|
|
5
4
|
remove_index :bigbluebutton_rooms, :server_id
|
|
5
|
+
remove_column :bigbluebutton_rooms, :server_id
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def self.down
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
class BigbluebuttonRailsTo220B < ActiveRecord::Migration
|
|
2
|
+
def up
|
|
3
|
+
BigbluebuttonMeeting.where(create_time: nil).find_each do |meeting|
|
|
4
|
+
meeting.update_attribute(create_time, meeting.start_time.to_i)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
remove_column :bigbluebutton_meetings, :start_time
|
|
8
|
+
|
|
9
|
+
add_column :bigbluebutton_recordings, :temp_start_time, :decimal, precision: 14, scale: 0
|
|
10
|
+
add_column :bigbluebutton_recordings, :temp_end_time, :decimal, precision: 14, scale: 0
|
|
11
|
+
|
|
12
|
+
BigbluebuttonRecording.find_each do |rec|
|
|
13
|
+
rec.update_attributes(
|
|
14
|
+
temp_start_time: rec.start_time.to_i,
|
|
15
|
+
temp_end_time: rec.end_time.to_i
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
remove_column :bigbluebutton_recordings, :start_time
|
|
20
|
+
remove_column :bigbluebutton_recordings, :end_time
|
|
21
|
+
|
|
22
|
+
rename_column :bigbluebutton_recordings, :temp_start_time, :start_time
|
|
23
|
+
rename_column :bigbluebutton_recordings, :temp_end_time, :end_time
|
|
24
|
+
|
|
25
|
+
BigbluebuttonRecording.find_each do |rec|
|
|
26
|
+
rec.update_attributes(
|
|
27
|
+
meeting_id: BigbluebuttonRecording.find_matching_meeting(rec).try(:id)
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
BigbluebuttonRecording.where(meeting_id: nil).find_each do |rec|
|
|
32
|
+
rec.update_attributes(
|
|
33
|
+
meeting_id: find_matching_meeting_closer_create_time(rec)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
BigbluebuttonRecording.where(meeting_id: nil).where.not(room_id: nil).find_each do |rec|
|
|
38
|
+
meeting = BigbluebuttonMeeting.create do |m|
|
|
39
|
+
creator_data = find_creator_data(rec)
|
|
40
|
+
m.server_id = rec.server_id
|
|
41
|
+
m.room_id = rec.room_id
|
|
42
|
+
m.meetingid = rec.meetingid
|
|
43
|
+
m.name = rec.name
|
|
44
|
+
m.running = false
|
|
45
|
+
m.recorded = true
|
|
46
|
+
m.creator_id = creator_data[0]
|
|
47
|
+
m.creator_name = creator_data[1]
|
|
48
|
+
m.server_url = nil
|
|
49
|
+
m.server_secret = nil
|
|
50
|
+
m.create_time = rec.start_time
|
|
51
|
+
m.ended = true
|
|
52
|
+
end
|
|
53
|
+
rec.update_attributes(meeting_id: meeting.id)
|
|
54
|
+
puts "Created a meeting for the recording id-#{rec.id}: Meeting id-#{meeting.id}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
create_table :bigbluebutton_attendees do |t|
|
|
58
|
+
t.string :user_id
|
|
59
|
+
t.string :external_user_id
|
|
60
|
+
t.string :user_name
|
|
61
|
+
t.decimal :join_time, precision: 14, scale: 0
|
|
62
|
+
t.decimal :left_time, precision: 14, scale: 0
|
|
63
|
+
t.integer :bigbluebutton_meeting_id
|
|
64
|
+
t.timestamps
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
add_column :bigbluebutton_meetings, :finish_time, :decimal, precision: 14, scale: 0
|
|
68
|
+
add_column :bigbluebutton_meetings, :got_stats, :string
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def find_matching_meeting_closer_create_time(recording)
|
|
72
|
+
meeting_id = recording.meeting_id
|
|
73
|
+
if meeting_id.nil?
|
|
74
|
+
meeting = BigbluebuttonMeeting.where("meetingid = ? AND created_at > ? AND created_at < ?",
|
|
75
|
+
recording.meetingid, Time.at(recording.start_time)-2.minutes, Time.at(recording.start_time)+2.minutes).first
|
|
76
|
+
|
|
77
|
+
unless meeting.nil?
|
|
78
|
+
unless BigbluebuttonRecording.find_by(meeting_id: meeting.id).present?
|
|
79
|
+
meeting_id = meeting.id
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
unless meeting_id.nil?
|
|
84
|
+
puts "Meeting found for recording id-#{recording.id}: Meeting id-#{meeting.id}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
meeting_id
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def find_creator_data(recording)
|
|
92
|
+
creator_data = []
|
|
93
|
+
if recording.room.owner.is_a?(User)
|
|
94
|
+
creator_id = recording.room.try(:owner).try(:id)
|
|
95
|
+
creator_name = recording.room.try(:owner).try(:name)
|
|
96
|
+
elsif recording.room.owner.is_a?(Space)
|
|
97
|
+
creator_id = recording.room.try(:owner).try(:admins).try(:first).try(:id)
|
|
98
|
+
creator_name = recording.room.try(:owner).try(:admins).try(:first).try(:name)
|
|
99
|
+
end
|
|
100
|
+
creator_data = [creator_id, creator_name]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def down
|
|
104
|
+
raise ActiveRecord::IrreversibleMigration, "Can't undo due to loss of values during migration"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class BigbluebuttonRailsTo230 < ActiveRecord::Migration
|
|
2
|
+
def self.up
|
|
3
|
+
rename_column :bigbluebutton_rooms, :param, :slug
|
|
4
|
+
rename_column :bigbluebutton_servers, :param, :slug
|
|
5
|
+
add_column :bigbluebutton_recordings, :recording_users, :text
|
|
6
|
+
add_column :bigbluebutton_playback_types, :downloadable, :boolean, default: false
|
|
7
|
+
remove_column :bigbluebutton_meetings, :got_stats
|
|
8
|
+
|
|
9
|
+
BigbluebuttonPlaybackType.find_each do |type|
|
|
10
|
+
downloadable = BigbluebuttonRails.configuration.downloadable_playback_types.include?(type.identifier)
|
|
11
|
+
type.update_attributes(downloadable: downloadable)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.down
|
|
16
|
+
remove_column :bigbluebutton_playback_types, :downloadable
|
|
17
|
+
remove_column :bigbluebutton_recordings, :recording_users
|
|
18
|
+
rename_column :bigbluebutton_servers, :slug, :param
|
|
19
|
+
rename_column :bigbluebutton_rooms, :slug, :param
|
|
20
|
+
add_column :bigbluebutton_meetings, :got_stats, :string
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'bigbluebutton_api'
|
|
3
|
+
|
|
4
|
+
describe Bigbluebutton::Api::RoomsController do
|
|
5
|
+
render_views
|
|
6
|
+
let!(:room) { FactoryGirl.create(:bigbluebutton_room) }
|
|
7
|
+
|
|
8
|
+
before {
|
|
9
|
+
@previous = BigbluebuttonRails.configuration.api_secret
|
|
10
|
+
BigbluebuttonRails.configuration.api_secret = "" # all allowed
|
|
11
|
+
}
|
|
12
|
+
after {
|
|
13
|
+
BigbluebuttonRails.configuration.api_secret = @previous
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
shared_examples "an authenticated API call" do
|
|
17
|
+
|
|
18
|
+
context "when the server has a secret" do
|
|
19
|
+
before {
|
|
20
|
+
@previous = BigbluebuttonRails.configuration.api_secret
|
|
21
|
+
BigbluebuttonRails.configuration.api_secret = "123123"
|
|
22
|
+
}
|
|
23
|
+
after {
|
|
24
|
+
BigbluebuttonRails.configuration.api_secret = @previous
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
[nil, "WRONG", '', "bearer 123123"].each do |auth_header|
|
|
28
|
+
context "forbids the authorization header #{auth_header.inspect}" do
|
|
29
|
+
before(:each) {
|
|
30
|
+
request.headers['Authorization'] = auth_header
|
|
31
|
+
action
|
|
32
|
+
}
|
|
33
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('403') }
|
|
34
|
+
it {
|
|
35
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
36
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.forbidden.title'))
|
|
37
|
+
}
|
|
38
|
+
it {
|
|
39
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
40
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.forbidden.msg'))
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "when the valid secret is informed" do
|
|
46
|
+
before(:each) {
|
|
47
|
+
request.headers['Authorization'] = "Bearer 123123"
|
|
48
|
+
action
|
|
49
|
+
}
|
|
50
|
+
it { JSON.parse(response.body)['errors'].should be_nil }
|
|
51
|
+
it { JSON.parse(response.body)['data'].should_not be_nil }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context "when the server has a nil secret" do
|
|
56
|
+
before {
|
|
57
|
+
@previous = BigbluebuttonRails.configuration.api_secret
|
|
58
|
+
BigbluebuttonRails.configuration.api_secret = nil
|
|
59
|
+
}
|
|
60
|
+
after {
|
|
61
|
+
BigbluebuttonRails.configuration.api_secret = @previous
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[nil, "WRONG", '', "bearer 123123"].each do |auth_header|
|
|
65
|
+
context "forbids the authorization header #{auth_header.inspect}" do
|
|
66
|
+
before(:each) {
|
|
67
|
+
request.headers['Authorization'] = auth_header
|
|
68
|
+
action
|
|
69
|
+
}
|
|
70
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('403') }
|
|
71
|
+
it {
|
|
72
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
73
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.forbidden.title'))
|
|
74
|
+
}
|
|
75
|
+
it {
|
|
76
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
77
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.forbidden.msg'))
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
context "when the server has an empty string as secret" do
|
|
84
|
+
before {
|
|
85
|
+
@previous = BigbluebuttonRails.configuration.api_secret
|
|
86
|
+
BigbluebuttonRails.configuration.api_secret = ''
|
|
87
|
+
}
|
|
88
|
+
after {
|
|
89
|
+
BigbluebuttonRails.configuration.api_secret = @previous
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[nil, "WRONG", '', "bearer 123123"].each do |auth_header|
|
|
93
|
+
context "forbids the authorization header #{auth_header.inspect}" do
|
|
94
|
+
before(:each) {
|
|
95
|
+
request.headers['Authorization'] = auth_header
|
|
96
|
+
action
|
|
97
|
+
}
|
|
98
|
+
it { JSON.parse(response.body)['errors'].should be_nil }
|
|
99
|
+
it { JSON.parse(response.body)['data'].should_not be_nil }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe "#index" do
|
|
107
|
+
context "authenticates" do
|
|
108
|
+
let(:action) { get :index, format: :json }
|
|
109
|
+
it_should_behave_like "an authenticated API call"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context "basic" do
|
|
113
|
+
before(:each) { get :index, format: :json }
|
|
114
|
+
it { should respond_with(:success) }
|
|
115
|
+
it { should respond_with_content_type('json') }
|
|
116
|
+
it { should assign_to(:rooms).with([room]) }
|
|
117
|
+
it { JSON.parse(response.body)['data'].should be_an_instance_of(Array) }
|
|
118
|
+
it { JSON.parse(response.body)['data'][0]['type'].should eql('room') }
|
|
119
|
+
it { JSON.parse(response.body)['data'][0]['id'].should eql(room.to_param) }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context "content" do
|
|
123
|
+
let(:owner) { FactoryGirl.create(:bigbluebutton_server) } # could be any model
|
|
124
|
+
before { room.update_attributes(owner: owner) }
|
|
125
|
+
before(:each) { get :index, format: :json }
|
|
126
|
+
|
|
127
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room.name) }
|
|
128
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['private'].should eql(room.private) }
|
|
129
|
+
it { JSON.parse(response.body)['data'][0]['links']['self'].should eql(room.short_path) }
|
|
130
|
+
|
|
131
|
+
context "includes the owner in the response" do
|
|
132
|
+
it { JSON.parse(response.body)['data'][0]['relationships']['owner']['data']['type'].should eql('server') }
|
|
133
|
+
it { JSON.parse(response.body)['data'][0]['relationships']['owner']['data']['id'].should eql(owner.to_param) }
|
|
134
|
+
it { JSON.parse(response.body)['data'][0]['relationships']['owner']['data']['attributes']['name'].should eql(owner.name) }
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context "empty response" do
|
|
139
|
+
before { room.destroy }
|
|
140
|
+
before(:each) { get :index, format: :json }
|
|
141
|
+
it { JSON.parse(response.body)['data'].should be_empty }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
context "filtering" do
|
|
145
|
+
before { room.update_attributes(name: "La Lo", param: "lalo-1") }
|
|
146
|
+
let!(:room2) { FactoryGirl.create(:bigbluebutton_room, name: "La Le", param: "lale-2") }
|
|
147
|
+
let!(:room3) { FactoryGirl.create(:bigbluebutton_room, name: "Li Lo", param: "lilo") }
|
|
148
|
+
|
|
149
|
+
context "filters by terms" do
|
|
150
|
+
before(:each) { get :index, filter: { terms: 'la' }, format: :json }
|
|
151
|
+
it { JSON.parse(response.body)['data'].length.should be(2) }
|
|
152
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql("La Le") }
|
|
153
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql("La Lo") }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context "orders by number of matches" do
|
|
157
|
+
before(:each) { get :index, filter: { terms: 'la,1' }, format: :json }
|
|
158
|
+
it { JSON.parse(response.body)['data'].length.should be(2) }
|
|
159
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql("La Lo") }
|
|
160
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql("La Le") }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
context "strips the terms" do
|
|
164
|
+
before(:each) { get :index, filter: { terms: ' la ' }, format: :json }
|
|
165
|
+
it { JSON.parse(response.body)['data'].length.should be(2) }
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
context "sorting" do
|
|
170
|
+
let!(:room2) { FactoryGirl.create(:bigbluebutton_room, name: room.name + "-2") }
|
|
171
|
+
let!(:room3) { FactoryGirl.create(:bigbluebutton_room, name: room.name + "-3") }
|
|
172
|
+
|
|
173
|
+
context "orders by name" do
|
|
174
|
+
before(:each) { get :index, sort: 'name', format: :json }
|
|
175
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room.name) }
|
|
176
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(room2.name) }
|
|
177
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(room3.name) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
context "orders by name DESC" do
|
|
181
|
+
before(:each) { get :index, sort: '-name', format: :json }
|
|
182
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room3.name) }
|
|
183
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(room2.name) }
|
|
184
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(room.name) }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
context "orders by name by default" do
|
|
188
|
+
before(:each) { get :index, format: :json }
|
|
189
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room.name) }
|
|
190
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(room2.name) }
|
|
191
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(room3.name) }
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context "orders by activity" do
|
|
195
|
+
before {
|
|
196
|
+
FactoryGirl.create(:bigbluebutton_meeting, create_time: Time.now - 2.hours, room: room)
|
|
197
|
+
FactoryGirl.create(:bigbluebutton_meeting, create_time: Time.now, room: room2)
|
|
198
|
+
FactoryGirl.create(:bigbluebutton_meeting, create_time: Time.now - 1.hour, room: room3)
|
|
199
|
+
}
|
|
200
|
+
before(:each) { get :index, sort: 'activity', format: :json }
|
|
201
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room2.name) }
|
|
202
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(room3.name) }
|
|
203
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(room.name) }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
context "orders by activity DESC" do
|
|
207
|
+
before {
|
|
208
|
+
FactoryGirl.create(:bigbluebutton_meeting, create_time: Time.now - 2.hours, room: room)
|
|
209
|
+
FactoryGirl.create(:bigbluebutton_meeting, create_time: Time.now, room: room2)
|
|
210
|
+
FactoryGirl.create(:bigbluebutton_meeting, create_time: Time.now - 1.hour, room: room3)
|
|
211
|
+
}
|
|
212
|
+
before(:each) { get :index, sort: '-activity', format: :json }
|
|
213
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room.name) }
|
|
214
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(room3.name) }
|
|
215
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(room2.name) }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
context "doesn't order by anything else" do
|
|
219
|
+
before {
|
|
220
|
+
FactoryGirl.create(:bigbluebutton_room, attendee_key: "2")
|
|
221
|
+
FactoryGirl.create(:bigbluebutton_room, attendee_key: "1")
|
|
222
|
+
FactoryGirl.create(:bigbluebutton_room, attendee_key: "0")
|
|
223
|
+
}
|
|
224
|
+
before(:each) { get :index, sort: 'attendee_key', format: :json }
|
|
225
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(room.name) }
|
|
226
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(room2.name) }
|
|
227
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(room3.name) }
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
context "pagination" do
|
|
232
|
+
context "limits to 10 by default" do
|
|
233
|
+
before {
|
|
234
|
+
15.times { FactoryGirl.create(:bigbluebutton_room) }
|
|
235
|
+
}
|
|
236
|
+
before(:each) { get :index, format: :json }
|
|
237
|
+
it { JSON.parse(response.body)['data'].length.should be(10) }
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
context "paginates" do
|
|
241
|
+
before {
|
|
242
|
+
9.times { FactoryGirl.create(:bigbluebutton_room) }
|
|
243
|
+
@rooms = BigbluebuttonRoom.order('name').all
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
context "returns the selected page" do
|
|
247
|
+
before(:each) { get :index, page: { size: 2, number: 3 }, format: :json }
|
|
248
|
+
it { JSON.parse(response.body)['data'].length.should be(2) }
|
|
249
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(@rooms[4].name) }
|
|
250
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(@rooms[5].name) }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
context "returns the first page by default" do
|
|
254
|
+
before(:each) { get :index, page: { size: 3 }, format: :json }
|
|
255
|
+
it { JSON.parse(response.body)['data'].length.should be(3) }
|
|
256
|
+
it { JSON.parse(response.body)['data'][0]['attributes']['name'].should eql(@rooms[0].name) }
|
|
257
|
+
it { JSON.parse(response.body)['data'][1]['attributes']['name'].should eql(@rooms[1].name) }
|
|
258
|
+
it { JSON.parse(response.body)['data'][2]['attributes']['name'].should eql(@rooms[2].name) }
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
context "includes the pagination links in the response" do
|
|
262
|
+
before(:each) { get :index, page: { size: 2, number: 3 }, format: :json }
|
|
263
|
+
it { JSON.parse(response.body)['links']['self'].should eql(request.original_url) }
|
|
264
|
+
it {
|
|
265
|
+
uri = URI.parse(request.original_url)
|
|
266
|
+
query = Rack::Utils.parse_query(uri.query)
|
|
267
|
+
query["page[number]"] = 4
|
|
268
|
+
uri.query = Rack::Utils.build_query(query)
|
|
269
|
+
JSON.parse(response.body)['links']['next'].should eql(uri.to_s)
|
|
270
|
+
}
|
|
271
|
+
it {
|
|
272
|
+
uri = URI.parse(request.original_url)
|
|
273
|
+
query = Rack::Utils.parse_query(uri.query)
|
|
274
|
+
query["page[number]"] = 2
|
|
275
|
+
uri.query = Rack::Utils.build_query(query)
|
|
276
|
+
JSON.parse(response.body)['links']['prev'].should eql(uri.to_s)
|
|
277
|
+
}
|
|
278
|
+
it {
|
|
279
|
+
uri = URI.parse(request.original_url)
|
|
280
|
+
query = Rack::Utils.parse_query(uri.query)
|
|
281
|
+
query["page[number]"] = 1
|
|
282
|
+
uri.query = Rack::Utils.build_query(query)
|
|
283
|
+
JSON.parse(response.body)['links']['first'].should eql(uri.to_s)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
context "doesn't include 'prev' when in the first page" do
|
|
287
|
+
before(:each) { get :index, page: { size: 2, number: 1 }, format: :json }
|
|
288
|
+
it { JSON.parse(response.body)['links']['prev'].should be_nil }
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
describe "#running" do
|
|
296
|
+
before {
|
|
297
|
+
mock_server_and_api
|
|
298
|
+
@api_mock.stub(:is_meeting_running?)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
context "authenticates" do
|
|
302
|
+
let(:action) { get :running, id: room.to_param, format: :json }
|
|
303
|
+
it_should_behave_like "an authenticated API call"
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
context "basic" do
|
|
307
|
+
before(:each) { get :running, id: room.to_param, format: :json }
|
|
308
|
+
it { should respond_with(:success) }
|
|
309
|
+
it { should respond_with_content_type('json') }
|
|
310
|
+
it { should assign_to(:room).with(room) }
|
|
311
|
+
it { JSON.parse(response.body)['data']['type'].should eql('room') }
|
|
312
|
+
it { JSON.parse(response.body)['data']['id'].should eql(room.to_param) }
|
|
313
|
+
it { room.request_headers["x-forwarded-for"].should eql(request.remote_ip) }
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
context "room is running" do
|
|
317
|
+
before { @api_mock.stub(:is_meeting_running?).and_return(true) }
|
|
318
|
+
before(:each) { get :running, id: room.to_param, format: :json }
|
|
319
|
+
it { JSON.parse(response.body)['data']['attributes']['running'].should be(true) }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
context "room is running" do
|
|
323
|
+
before { @api_mock.should_receive(:is_meeting_running?).and_return(false) }
|
|
324
|
+
before(:each) { get :running, id: room.to_param, format: :json }
|
|
325
|
+
it { JSON.parse(response.body)['data']['attributes']['running'].should be(false) }
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
context "when the room is not found" do
|
|
329
|
+
before { BigbluebuttonRoom.stub(:find_by).and_return(nil) }
|
|
330
|
+
before(:each) { post :running, id: room.to_param + '-2', format: :json }
|
|
331
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('404') }
|
|
332
|
+
it {
|
|
333
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
334
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.room_not_found.title'))
|
|
335
|
+
}
|
|
336
|
+
it {
|
|
337
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
338
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.room_not_found.msg'))
|
|
339
|
+
}
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
describe "#join" do
|
|
344
|
+
let(:expected_url) { 'https://fake-url.no/join?anything=1' }
|
|
345
|
+
before {
|
|
346
|
+
mock_server_and_api
|
|
347
|
+
@api_mock.stub(:is_meeting_running?).and_return(true)
|
|
348
|
+
@api_mock.stub(:join_meeting_url).and_return(expected_url)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
context "authenticates" do
|
|
352
|
+
let(:action) { post :join, id: room.to_param, format: :json, name: 'User 1' }
|
|
353
|
+
it_should_behave_like "an authenticated API call"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
context "basic" do
|
|
357
|
+
before { room.update_attributes(private: false) }
|
|
358
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1' }
|
|
359
|
+
it { should respond_with(:success) }
|
|
360
|
+
it { should respond_with_content_type('json') }
|
|
361
|
+
it { should assign_to(:room).with(room) }
|
|
362
|
+
it { should assign_to(:url).with(expected_url) }
|
|
363
|
+
it { JSON.parse(response.body)['data']['type'].should eql('join-url') }
|
|
364
|
+
it { JSON.parse(response.body)['data']['id'].should eql(expected_url) }
|
|
365
|
+
it { room.request_headers["x-forwarded-for"].should eql(request.remote_ip) }
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
context "generates the correct join url" do
|
|
369
|
+
let(:expected_url) { 'https://another-fake-url.no/join?anything=1' }
|
|
370
|
+
|
|
371
|
+
context "attendee in a public room" do
|
|
372
|
+
before {
|
|
373
|
+
room.should_receive(:parameterized_join_url).with('User 1', :attendee, nil, {}).and_return(expected_url)
|
|
374
|
+
}
|
|
375
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1' }
|
|
376
|
+
it { JSON.parse(response.body)['data']['id'].should eql(expected_url) }
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
context "attendee in a private room" do
|
|
380
|
+
before {
|
|
381
|
+
room.update_attributes(private: true)
|
|
382
|
+
room.should_receive(:parameterized_join_url).with('User 1', :attendee, nil, {}).and_return(expected_url)
|
|
383
|
+
}
|
|
384
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1', key: room.attendee_key }
|
|
385
|
+
it { JSON.parse(response.body)['data']['id'].should eql(expected_url) }
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
context "moderator in a private room" do
|
|
389
|
+
before {
|
|
390
|
+
room.update_attributes(private: true)
|
|
391
|
+
room.should_receive(:parameterized_join_url).with('User 1', :moderator, nil, {}).and_return(expected_url)
|
|
392
|
+
}
|
|
393
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1', key: room.moderator_key }
|
|
394
|
+
it { JSON.parse(response.body)['data']['id'].should eql(expected_url) }
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
context "with metadata" do
|
|
398
|
+
let(:expected_meta) {
|
|
399
|
+
{ 'userdata-param-1' => 1, 'userdata-param_2' => 'string-2' }
|
|
400
|
+
}
|
|
401
|
+
before {
|
|
402
|
+
room.should_receive(:parameterized_join_url).with('User 1', :attendee, nil, expected_meta).and_return(expected_url)
|
|
403
|
+
}
|
|
404
|
+
before(:each) {
|
|
405
|
+
post :join, id: room.to_param, format: :json, name: 'User 1', key: room.moderator_key,
|
|
406
|
+
'meta-param-1': 1, meta_param_2: 'string-2'
|
|
407
|
+
}
|
|
408
|
+
it { JSON.parse(response.body)['data']['id'].should eql(expected_url) }
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
context "when the room is not found" do
|
|
413
|
+
before { BigbluebuttonRoom.stub(:find_by).and_return(nil) }
|
|
414
|
+
before(:each) { post :join, id: room.to_param + '-2', format: :json, name: 'User 1' }
|
|
415
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('404') }
|
|
416
|
+
it {
|
|
417
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
418
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.room_not_found.title'))
|
|
419
|
+
}
|
|
420
|
+
it {
|
|
421
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
422
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.room_not_found.msg'))
|
|
423
|
+
}
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
context "when the room is not running" do
|
|
427
|
+
before { @api_mock.stub(:is_meeting_running?).and_return(false) }
|
|
428
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1' }
|
|
429
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('400') }
|
|
430
|
+
it {
|
|
431
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
432
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.room_not_running.title'))
|
|
433
|
+
}
|
|
434
|
+
it {
|
|
435
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
436
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.room_not_running.msg'))
|
|
437
|
+
}
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
context "when a name is not informed" do
|
|
441
|
+
before(:each) { post :join, id: room.to_param, format: :json }
|
|
442
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('400') }
|
|
443
|
+
it {
|
|
444
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
445
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.missing_params.title'))
|
|
446
|
+
}
|
|
447
|
+
it {
|
|
448
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
449
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.missing_params.msg'))
|
|
450
|
+
}
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
context "when a key is not informed and the room is private" do
|
|
454
|
+
before { room.update_attributes(private: true) }
|
|
455
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1' }
|
|
456
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('400') }
|
|
457
|
+
it {
|
|
458
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
459
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.missing_params.title'))
|
|
460
|
+
}
|
|
461
|
+
it {
|
|
462
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
463
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.missing_params.msg'))
|
|
464
|
+
}
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
context "attendee in a private room with wrong key" do
|
|
468
|
+
before { room.update_attributes(private: true) }
|
|
469
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1', key: 'WRONG' }
|
|
470
|
+
it { JSON.parse(response.body)['errors'][0]['status'].should eql('403') }
|
|
471
|
+
it {
|
|
472
|
+
title = JSON.parse(response.body)['errors'][0]['title']
|
|
473
|
+
title.should eql(I18n.t('bigbluebutton_rails.api.errors.invalid_key.title'))
|
|
474
|
+
}
|
|
475
|
+
it {
|
|
476
|
+
detail = JSON.parse(response.body)['errors'][0]['detail']
|
|
477
|
+
detail.should eql(I18n.t('bigbluebutton_rails.api.errors.invalid_key.msg'))
|
|
478
|
+
}
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
context "when guest support is on" do
|
|
482
|
+
let(:expected_url) { 'https://another-fake-url.no/join?anything=1' }
|
|
483
|
+
before {
|
|
484
|
+
@guest_support = BigbluebuttonRails.configuration.guest_support
|
|
485
|
+
BigbluebuttonRails.configuration.guest_support = true
|
|
486
|
+
}
|
|
487
|
+
after {
|
|
488
|
+
BigbluebuttonRails.configuration.guest_support = @guest_support
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
before {
|
|
492
|
+
room.should_receive(:parameterized_join_url).with('User 1', :guest, nil, {}).and_return(expected_url)
|
|
493
|
+
}
|
|
494
|
+
before(:each) { post :join, id: room.to_param, format: :json, name: 'User 1' }
|
|
495
|
+
it { JSON.parse(response.body)['data']['id'].should eql(expected_url) }
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|