From fe5a60b5c0261205564fcbe583c8b1eb4d9ca434 Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Thu, 20 May 2021 18:21:18 -0300 Subject: [PATCH 01/15] Run more times the worker that monitors recordings for a room It would run a few times following an specific set of intervals. Now the intervals are configurable and the worker will run more times. It starts monitoring recordings once every few minutes (5-10m) and gets progressively slower. At the end it runs only once every 3h. In total it will run for about 24h. This help us track playback formats that might take hours to be available. --- app/models/bigbluebutton_room.rb | 8 +++---- ...igbluebutton_recordings_for_room_worker.rb | 21 +++++++++++++------ lib/bigbluebutton_rails/configuration.rb | 14 +++++++++++++ spec/models/bigbluebutton_room_spec.rb | 3 ++- ...ebutton_recordings_for_room_worker_spec.rb | 9 +++++--- 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/app/models/bigbluebutton_room.rb b/app/models/bigbluebutton_room.rb index 24619700..75e414d1 100644 --- a/app/models/bigbluebutton_room.rb +++ b/app/models/bigbluebutton_room.rb @@ -395,11 +395,9 @@ def finish_meetings if to_be_finished.count > 0 # start trying to get the recording for this room - # since we don't have a way to know exactly when a recording is done, we - # have to keep polling the server for them - # 3 times so it tries at: 4, 9, 14 and 19 - # no point trying more since there is a global synchronization process - Resque.enqueue_in(1.minutes, ::BigbluebuttonRecordingsForRoomWorker, self.id, 10) + intervals = BigbluebuttonRails.configuration.recording_sync_for_room_intervals + tries = intervals.length - 1 + Resque.enqueue_in(intervals[0], ::BigbluebuttonRecordingsForRoomWorker, self.id, tries) end end diff --git a/app/workers/bigbluebutton_recordings_for_room_worker.rb b/app/workers/bigbluebutton_recordings_for_room_worker.rb index 6f738dc6..feebc760 100644 --- a/app/workers/bigbluebutton_recordings_for_room_worker.rb +++ b/app/workers/bigbluebutton_recordings_for_room_worker.rb @@ -12,16 +12,25 @@ class BigbluebuttonRecordingsForRoomWorker @queue = :bigbluebutton_rails def self.perform(room_id, tries_left=0) - Rails.logger.info "BigbluebuttonRecordingsForRoomWorker worker running" + return if tries_left <= 0 + + Rails.logger.info "BigbluebuttonRecordingsForRoomWorker worker running " \ + "room_id=#{room_id} tries_left=#{tries_left}" room = BigbluebuttonRoom.find(room_id) if room.present? - Rails.logger.info "BigbluebuttonRecordingsForRoomWorker getting recordings for #{room.inspect}" - room.fetch_recordings( state: BigbluebuttonRecording::STATES.values) + Rails.logger.info "BigbluebuttonRecordingsForRoomWorker getting recordings for meetingid=#{room.meetingid}" + + room.fetch_recordings(state: BigbluebuttonRecording::STATES.values) - if tries_left > 0 - Resque.enqueue_in(5.minutes, ::BigbluebuttonRecordingsForRoomWorker, room_id, tries_left - 1) - end + intervals = BigbluebuttonRails.configuration.recording_sync_for_room_intervals + idx = intervals.length - tries_left + wait = intervals[intervals.length - 1] if wait.nil? + + Resque.enqueue_in(wait, ::BigbluebuttonRecordingsForRoomWorker, room_id, tries_left - 1) end + + Rails.logger.info "BigbluebuttonRecordingsForRoomWorker worker ended " \ + "room_id=#{room_id} tries_left=#{tries_left}" end end diff --git a/lib/bigbluebutton_rails/configuration.rb b/lib/bigbluebutton_rails/configuration.rb index 0a08c17a..2ad74fb4 100644 --- a/lib/bigbluebutton_rails/configuration.rb +++ b/lib/bigbluebutton_rails/configuration.rb @@ -17,6 +17,7 @@ class Configuration attr_accessor :downloadable_playback_types attr_accessor :debug attr_accessor :api_timeout + attr_accessor :recording_sync_for_room_intervals # methods attr_accessor :select_server @@ -102,6 +103,19 @@ def initialize # Set it to an empty string to disable authentication. Set it to nil to deny all # requests (disable the API). @api_secret = nil + + # Sequence of intervals for the worker that monitors the recordings of a room after + # a meeting ends in that room. Sleep this amount of time between each `getRecordings` + # to the room. + # Start faster, get slower later on. Tries for a total of about 24h. + @recording_sync_for_room_intervals = [ + 1.minute, + 5.minutes, 5.minutes, + 10.minutes, 10.minutes, + 30.minutes, 30.minutes, 30.minutes, + 2.hours, 2.hours, + 3.hours, 3.hours, 3.hours, 3.hours, 3.hours, 3.hours + ] end def set_controllers(options) diff --git a/spec/models/bigbluebutton_room_spec.rb b/spec/models/bigbluebutton_room_spec.rb index dc97f703..465e1629 100644 --- a/spec/models/bigbluebutton_room_spec.rb +++ b/spec/models/bigbluebutton_room_spec.rb @@ -1419,7 +1419,8 @@ let!(:meeting1) { FactoryGirl.create(:bigbluebutton_meeting, room: room, ended: false, running: true) } let!(:meeting2) { FactoryGirl.create(:bigbluebutton_meeting, room: room, ended: false, running: true) } before { - expect(Resque).to receive(:enqueue_in).with(1.minute, ::BigbluebuttonRecordingsForRoomWorker, room.id, 10) + tries = BigbluebuttonRails.configuration.recording_sync_for_room_intervals.length - 1 + expect(Resque).to receive(:enqueue_in).with(1.minute, ::BigbluebuttonRecordingsForRoomWorker, room.id, tries) } it { room.finish_meetings } end diff --git a/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb b/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb index b6a67c05..877f02fd 100644 --- a/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb +++ b/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb @@ -14,18 +14,21 @@ BigbluebuttonRoom.stub(:find).and_return(room) expect(room).to receive(:fetch_recordings).once } - it { BigbluebuttonRecordingsForRoomWorker.perform(room.id) } + it { BigbluebuttonRecordingsForRoomWorker.perform(room.id, 1) } end context "if there are still tries left" do before { BigbluebuttonRoom.stub(:find).and_return(room) room.stub(:fetch_recordings) + + intervals = BigbluebuttonRails.configuration.recording_sync_for_room_intervals + wait = intervals[intervals.length - 6] expect(Resque).to receive(:enqueue_in) - .with(5.minutes, ::BigbluebuttonRecordingsForRoomWorker, room.id, 0) + .with(wait, ::BigbluebuttonRecordingsForRoomWorker, room.id, 6) .once } - it { BigbluebuttonRecordingsForRoomWorker.perform(room.id, 1) } + it { BigbluebuttonRecordingsForRoomWorker.perform(room.id, 7) } end context "if there are no more tries left" do From 15615cea55fa9abb6050c5598e001927fe9f2399 Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Sat, 22 May 2021 18:13:25 -0300 Subject: [PATCH 02/15] Better logs when syncing recordings --- app/models/bigbluebutton_recording.rb | 16 ++++++++++------ .../bigbluebutton_update_recordings_worker.rb | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/models/bigbluebutton_recording.rb b/app/models/bigbluebutton_recording.rb index d5b7288a..fe7c471a 100644 --- a/app/models/bigbluebutton_recording.rb +++ b/app/models/bigbluebutton_recording.rb @@ -158,21 +158,23 @@ def self.recording_changed?(recording, data) # # TODO: catch exceptions on creating/updating recordings def self.sync(server, recordings, full_sync=false) + + logger.info "Sync recordings: starting a sync for server=#{server.url};#{server.secret} full_sync=#{full_sync}" recordings.each do |rec| rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID]) rec_data = adapt_recording_hash(rec) - changed = !rec_obj.present? || - self.recording_changed?(rec_obj, rec_data) + changed = !rec_obj.present? || self.recording_changed?(rec_obj, rec_data) if changed + logger.info "Sync recordings: detected that the recording changed #{rec[:recordID]}" BigbluebuttonRecording.transaction do if rec_obj - logger.info "Sync recordings: updating recording #{rec_obj.inspect}" - logger.debug "Sync recordings: recording data #{rec_data.inspect}" + logger.info "Sync recordings: updating recording #{rec[:recordID]}" + logger.debug "Sync recordings: updating recording with data #{rec_data.inspect}" self.update_recording(server, rec_obj, rec_data) else - logger.info "Sync recordings: creating recording" - logger.debug "Sync recordings: recording data #{rec_data.inspect}" + logger.info "Sync recordings: creating recording #{rec[:recordID]}" + logger.debug "Sync recordings: creating recording with data #{rec_data.inspect}" self.create_recording(server, rec_data) end end @@ -200,6 +202,8 @@ def self.sync(server, recordings, full_sync=false) update_all(available: true) end end + + logger.info "Sync recordings: finished a sync for server=#{server.url};#{server.secret} full_sync=#{full_sync}" end protected diff --git a/app/workers/bigbluebutton_update_recordings_worker.rb b/app/workers/bigbluebutton_update_recordings_worker.rb index d8959863..28d840d0 100644 --- a/app/workers/bigbluebutton_update_recordings_worker.rb +++ b/app/workers/bigbluebutton_update_recordings_worker.rb @@ -7,5 +7,6 @@ class BigbluebuttonUpdateRecordingsWorker def self.perform(server_id=nil) Rails.logger.info "BigbluebuttonUpdateRecordingsWorker worker running" BigbluebuttonRails::BackgroundTasks.update_recordings(server_id) + Rails.logger.info "BigbluebuttonUpdateRecordingsWorker worker ended" end end From 45421e3eec7643a638bd608e59a47fe16714147a Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Sat, 22 May 2021 18:30:44 -0300 Subject: [PATCH 03/15] Add missing BigbluebuttonRecording#state on migration.rb Was included in v3.0.0 but not added to migration.rb. --- lib/generators/bigbluebutton_rails/templates/migration.rb | 1 + spec/factories/bigbluebutton_recording.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/generators/bigbluebutton_rails/templates/migration.rb b/lib/generators/bigbluebutton_rails/templates/migration.rb index 578055e4..f3274fbb 100644 --- a/lib/generators/bigbluebutton_rails/templates/migration.rb +++ b/lib/generators/bigbluebutton_rails/templates/migration.rb @@ -51,6 +51,7 @@ def self.up t.boolean :available, :default => true t.integer :size, limit: 8, default: 0 t.text :recording_users + t.string :bigbluebutton_recordings, :state t.timestamps end add_index :bigbluebutton_recordings, :room_id diff --git a/spec/factories/bigbluebutton_recording.rb b/spec/factories/bigbluebutton_recording.rb index 51232d2c..fabebb85 100644 --- a/spec/factories/bigbluebutton_recording.rb +++ b/spec/factories/bigbluebutton_recording.rb @@ -12,6 +12,7 @@ r.sequence(:recordid) { |n| "rec#{n}-#{SecureRandom.uuid}-#{DateTime.now.to_i}" } r.size { rand((20*1024**2)..(500*1024**2)) } # size ranging from 20Mb to 500Mb r.available true + r.state { ['processing', 'processed', 'published', 'unpublished'].sample } after(:create) do |r| r.updated_at = r.updated_at.change(:usec => 0) From 9128d35f2509589843ac0eb238ca48de2cd3ef74 Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Sat, 22 May 2021 18:38:47 -0300 Subject: [PATCH 04/15] Monitor :name and :state when checking if a recording changed More attributes to track. If they change in the web conference server we should update the recording in the local DB. --- app/models/bigbluebutton_recording.rb | 2 +- spec/models/bigbluebutton_recording_spec.rb | 57 +++++++++++++++------ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/app/models/bigbluebutton_recording.rb b/app/models/bigbluebutton_recording.rb index fe7c471a..c6929a83 100644 --- a/app/models/bigbluebutton_recording.rb +++ b/app/models/bigbluebutton_recording.rb @@ -106,7 +106,7 @@ def self.recording_changed?(recording, data) # the attributes that are considered in the comparison keys = [ # rawSize is not stored at the moment :end_time, :meetingid, :metadata, :playback, :published, - :recordid, :size, :start_time + :recordid, :size, :start_time, :state, :name ] keys_formats = [ # :size, :processingTime are not stored at the moment :length, :type, :url diff --git a/spec/models/bigbluebutton_recording_spec.rb b/spec/models/bigbluebutton_recording_spec.rb index 35fd77e7..90ac3c4b 100644 --- a/spec/models/bigbluebutton_recording_spec.rb +++ b/spec/models/bigbluebutton_recording_spec.rb @@ -165,6 +165,7 @@ start_time: DateTime.now, end_time: DateTime.now + 2.hours, size: "100", + state: "processing", metadata: { course: "Fundamentals of JAVA", description: "List of recordings", @@ -192,7 +193,8 @@ published: data[:published], start_time: data[:start_time], end_time: data[:end_time], - size: data[:size] + size: data[:size], + state: data[:state], } r = FactoryGirl.create(:bigbluebutton_recording, attrs) data[:metadata].each do |k, v| @@ -209,15 +211,41 @@ BigbluebuttonRecording.recording_changed?(recording, data).should be(false) end - context "returns true when an attribute changed" do - it "in the data" do - data[:published] = !data[:published] - BigbluebuttonRecording.recording_changed?(recording, data).should be(true) - end + tracked_attrs = [ + [ :end_time, :time ], + [ :start_time, :time ], + [ :meetingid, :string ], + [ :recordid, :string ], + [ :name, :string ], + [ :state, :string ], + [ :published, :boolean ], + [ :size, :string ] + ] + tracked_attrs.each do |attr| + context "returns true when the attribute #{attr[0]} changed" do + it "in the data" do + data[attr[0]] = case attr[1] + when :time + data[attr[0]] + 1.day + when :string + data[attr[0]] + "1" + when :boolean + !data[attr[0]] + end + BigbluebuttonRecording.recording_changed?(recording, data).should be(true) + end - it "in the database" do - recording.update(published: !recording.published) - BigbluebuttonRecording.recording_changed?(recording, data).should be(true) + it "in the database" do + case attr[1] + when :time + recording.update_attribute(attr[0], data[attr[0]] + 1.day) + when :string + recording.update_attribute(attr[0], data[attr[0]] + "1") + when :boolean + recording.update_attribute(attr[0], !data[attr[0]]) + end + BigbluebuttonRecording.recording_changed?(recording, data).should be(true) + end end end @@ -354,6 +382,7 @@ startTime: DateTime.now, endTime: DateTime.now + 2.hours, size: 100, + state: 'processing', metadata: { course: "Fundamentals of JAVA", description: "List of recordings", @@ -565,21 +594,17 @@ it "doesn't update if the recording didn't change" do # once to create the recording now = DateTime.now - expected = now - 1.month + expected = now - 1.day Timecop.freeze(expected) rec_data = data.clone - BigbluebuttonRecording.sync(new_server, rec_data) - # set an expected updated_at + # bug to the current timestamp rec = BigbluebuttonRecording.first - puts "before #{rec.updated_at} (#{DateTime.now})" - # rec.send(:write_attribute, :updated_at, expected) + Timecop.freeze(now) # shouldn't change it - Timecop.freeze(now) BigbluebuttonRecording.sync(new_server, rec_data) - puts "after #{rec.reload.updated_at} (#{DateTime.now})" rec.reload.updated_at.to_i.should eql(expected.to_i) Timecop.return From 7909058651403637c5e8864c9accccc1422c4eb4 Mon Sep 17 00:00:00 2001 From: JoaoCarAlmSilva Date: Mon, 24 May 2021 18:02:12 -0300 Subject: [PATCH 05/15] Fix setting unavailable by parallel execution of workers --- app/models/bigbluebutton_recording.rb | 4 +++- app/models/bigbluebutton_server.rb | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/bigbluebutton_recording.rb b/app/models/bigbluebutton_recording.rb index d5b7288a..023b89b8 100644 --- a/app/models/bigbluebutton_recording.rb +++ b/app/models/bigbluebutton_recording.rb @@ -155,9 +155,10 @@ def self.recording_changed?(recording, data) # if they are not in the array but instead mark them as unavailable. # 'server' is the BigbluebuttonServer object from which the recordings # were fetched. + # full_sync_started_at:: Date var to ensure consistency by execution # # TODO: catch exceptions on creating/updating recordings - def self.sync(server, recordings, full_sync=false) + def self.sync(server, recordings, full_sync=false, full_sync_started_at=nil) recordings.each do |rec| rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID]) rec_data = adapt_recording_hash(rec) @@ -193,6 +194,7 @@ def self.sync(server, recordings, full_sync=false) BigbluebuttonRecording. where(available: true, server: server). where.not(recordid: recordIDs). + where.not("end_time > ?", full_sync_started_at.to_i). update_all(available: false) BigbluebuttonRecording. where(available: false, server: server). diff --git a/app/models/bigbluebutton_server.rb b/app/models/bigbluebutton_server.rb index a4d29df7..325444e9 100644 --- a/app/models/bigbluebutton_server.rb +++ b/app/models/bigbluebutton_server.rb @@ -137,9 +137,12 @@ def send_delete_recordings(ids) def fetch_recordings(filter=nil, full_sync=false) filter ||= {} logger.info "Fetching recordings for the server #{self.inspect} with filter: #{filter.inspect}" + if full_sync + get_rec_to_sync_started_at = Time.new + end recordings = self.api.get_recordings(filter) if recordings and recordings[:recordings] - BigbluebuttonRecording.sync(self, recordings[:recordings], full_sync) + BigbluebuttonRecording.sync(self, recordings[:recordings], full_sync, get_rec_to_sync_started_at) end end From dd28276d4139d8abb2d43bd5d35c48c9ea9267c8 Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Tue, 25 May 2021 10:24:24 -0300 Subject: [PATCH 06/15] Fetch recordings for each room instead of each server on sync When synchronizing recordings, instead of iterating over the servers, iterate over all rooms. There are usually way more rooms in the DB, but their getRecordings call are way smaller than getting all recordings in a server. We're replacing a few (sometimes a single) getRecordings that might be too big and slow, with several smaller ones. The old method still exists and can be easily used by applications if needed. --- app/models/bigbluebutton_recording.rb | 33 +++--- app/models/bigbluebutton_room.rb | 8 +- app/models/bigbluebutton_server.rb | 11 +- ...igbluebutton_recordings_for_room_worker.rb | 2 +- .../bigbluebutton_update_recordings_worker.rb | 5 +- lib/bigbluebutton_rails/background_tasks.rb | 59 +++++++--- .../bigbluebutton/rooms_controller_spec.rb | 11 +- .../background_tasks_spec.rb | 107 ++++++++++++++++-- spec/models/bigbluebutton_recording_spec.rb | 26 ++++- spec/models/bigbluebutton_room_spec.rb | 27 +++++ spec/models/bigbluebutton_server_spec.rb | 50 ++++---- ...luebutton_update_recordings_worker_spec.rb | 2 +- 12 files changed, 257 insertions(+), 84 deletions(-) diff --git a/app/models/bigbluebutton_recording.rb b/app/models/bigbluebutton_recording.rb index c6929a83..ee66bcfb 100644 --- a/app/models/bigbluebutton_recording.rb +++ b/app/models/bigbluebutton_recording.rb @@ -157,9 +157,9 @@ def self.recording_changed?(recording, data) # were fetched. # # TODO: catch exceptions on creating/updating recordings - def self.sync(server, recordings, full_sync=false) + def self.sync(server, recordings, sync_scope=nil) + logger.info "Sync recordings: starting a sync for server=#{server.url};#{server.secret} sync_scope=\"#{sync_scope&.to_sql}\"" - logger.info "Sync recordings: starting a sync for server=#{server.url};#{server.secret} full_sync=#{full_sync}" recordings.each do |rec| rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID]) rec_data = adapt_recording_hash(rec) @@ -182,28 +182,29 @@ def self.sync(server, recordings, full_sync=false) end cleanup_playback_types - # set as unavailable the recordings that are not in 'recordings', but - # only in a full synchronization process, which means that the recordings - # in `recordings` are *all* available in `server`, not a subset. - if full_sync + # Set as unavailable the recordings that are not in the list returned by getRecordings, but + # only if there is a scope set, otherwise we don't know how the call to getRecordings was filtered. + # Uses the scope passed to figure out which recordings should be marked as unavailable. + if sync_scope.present? recordIDs = recordings.map{ |rec| rec[:recordID] } - if recordIDs.length <= 0 # empty response - BigbluebuttonRecording. - where(available: true, server: server). + if recordIDs.length <= 0 + # empty response, all recordings in the scope are unavailable + sync_scope. + where(available: true). update_all(available: false) else - BigbluebuttonRecording. - where(available: true, server: server). - where.not(recordid: recordIDs). + # non empty response, mark as unavailable all recordings in the scope that + # were not returned by getRecording + sync_scope. + where(available: true).where.not(recordid: recordIDs). update_all(available: false) - BigbluebuttonRecording. - where(available: false, server: server). - where(recordid: recordIDs). + sync_scope. + where(available: false, recordid: recordIDs). update_all(available: true) end end - logger.info "Sync recordings: finished a sync for server=#{server.url};#{server.secret} full_sync=#{full_sync}" + logger.info "Sync recordings: finished a sync for server=#{server.url};#{server.secret} sync_scope=\"#{sync_scope&.to_sql}\"" end protected diff --git a/app/models/bigbluebutton_room.rb b/app/models/bigbluebutton_room.rb index 75e414d1..30490aec 100644 --- a/app/models/bigbluebutton_room.rb +++ b/app/models/bigbluebutton_room.rb @@ -425,10 +425,14 @@ def self.generate_dial_number(pattern=nil) end end - def fetch_recordings(filter={}) + # Synchronizes all the recordings for this room. Will only get recordings with the + # default states (won't get recordings with the state 'deleted', for instance). + def fetch_recordings server = BigbluebuttonRails.configuration.select_server.call(self, :get_recordings) if server.present? - server.fetch_recordings(filter.merge({ meetingID: self.meetingid })) + states = BigbluebuttonRecording::STATES.values + scope = BigbluebuttonRecording.where(room: self, state: states) + server.fetch_recordings({ meetingID: self.meetingid, state: states }, scope) true else false diff --git a/app/models/bigbluebutton_server.rb b/app/models/bigbluebutton_server.rb index a4d29df7..69983d99 100644 --- a/app/models/bigbluebutton_server.rb +++ b/app/models/bigbluebutton_server.rb @@ -134,12 +134,17 @@ def send_delete_recordings(ids) # metadata values. # # Triggers API call: getRecordings. - def fetch_recordings(filter=nil, full_sync=false) + def fetch_recordings(filter=nil, sync_scope=nil) filter ||= {} - logger.info "Fetching recordings for the server #{self.inspect} with filter: #{filter.inspect}" + logger.info "Fetching recordings on #{self.url} with filter: #{filter.inspect}" recordings = self.api.get_recordings(filter) if recordings and recordings[:recordings] - BigbluebuttonRecording.sync(self, recordings[:recordings], full_sync) + + # if no scope is set and there are no filters, set the scope to all recordings + # in this server + sync_scope = BigbluebuttonRecording.where(server: self) if filter.blank? && sync_scope.nil? + + BigbluebuttonRecording.sync(self, recordings[:recordings], sync_scope) end end diff --git a/app/workers/bigbluebutton_recordings_for_room_worker.rb b/app/workers/bigbluebutton_recordings_for_room_worker.rb index feebc760..6f1d6c49 100644 --- a/app/workers/bigbluebutton_recordings_for_room_worker.rb +++ b/app/workers/bigbluebutton_recordings_for_room_worker.rb @@ -21,7 +21,7 @@ def self.perform(room_id, tries_left=0) if room.present? Rails.logger.info "BigbluebuttonRecordingsForRoomWorker getting recordings for meetingid=#{room.meetingid}" - room.fetch_recordings(state: BigbluebuttonRecording::STATES.values) + room.fetch_recordings intervals = BigbluebuttonRails.configuration.recording_sync_for_room_intervals idx = intervals.length - tries_left diff --git a/app/workers/bigbluebutton_update_recordings_worker.rb b/app/workers/bigbluebutton_update_recordings_worker.rb index 28d840d0..36725779 100644 --- a/app/workers/bigbluebutton_update_recordings_worker.rb +++ b/app/workers/bigbluebutton_update_recordings_worker.rb @@ -6,7 +6,10 @@ class BigbluebuttonUpdateRecordingsWorker def self.perform(server_id=nil) Rails.logger.info "BigbluebuttonUpdateRecordingsWorker worker running" - BigbluebuttonRails::BackgroundTasks.update_recordings(server_id) + + # TODO: configurable filter of rooms + BigbluebuttonRails::BackgroundTasks.update_recordings_by_room + Rails.logger.info "BigbluebuttonUpdateRecordingsWorker worker ended" end end diff --git a/lib/bigbluebutton_rails/background_tasks.rb b/lib/bigbluebutton_rails/background_tasks.rb index 0f0021f5..b28a5ea9 100644 --- a/lib/bigbluebutton_rails/background_tasks.rb +++ b/lib/bigbluebutton_rails/background_tasks.rb @@ -17,23 +17,54 @@ def self.finish_meetings end end - # Updates the recordings for all servers if `server_id` is nil or or for the + # Updates the recordings for all servers if `server_id` is nil or for the # server with id `server_id`. - def self.update_recordings(server_id=nil) - Rails.logger.info "BackgroundTasks: Starting the update of recordings for all servers" - BigbluebuttonServer.find_each do |server| - begin - if server_id.nil? || server_id == server.id - server.fetch_recordings(nil, true) - Rails.logger.info "BackgroundTasks: List of recordings from #{server.url} updated successfully" - end - rescue StandardError => e - Rails.logger.info "BackgroundTasks: Failure fetching recordings from #{server.inspect}" - Rails.logger.info "BackgroundTasks: #{e.inspect}" - Rails.logger.info "BackgroundTasks: #{e.backtrace.join("\n")}" + def self.update_recordings_by_server(server=nil) + Rails.logger.info "BackgroundTasks: Starting the update of recordings by server server=#{server&.url};#{server&.secret}" + + if server.nil? + BigbluebuttonServer.find_each do |server| + update_recordings_for_server(server) end + else + update_recordings_for_server(server) + end + + Rails.logger.info "BackgroundTasks: Ended the update of recordings by server server=#{server&.url};#{server&.secret}" + end + + # Updates the recordings for all rooms if `query` is nil or will use `query` to fetch the rooms + # that should be updated. + def self.update_recordings_by_room(query=nil) + query_s = "query=\"#{query&.to_sql}\"" + Rails.logger.info "BackgroundTasks: Starting the update of recordings by room #{query_s}" + + query = BigbluebuttonRoom if query.blank? + query.find_each do |room| + update_recordings_for_room(room) + end + + Rails.logger.info "BackgroundTasks: Ended the update of recordings by room #{query_s}" + end + + def self.update_recordings_for_server(server) + begin + server.fetch_recordings + Rails.logger.info "BackgroundTasks: List of recordings for #{server.url} updated successfully" + rescue StandardError => e + Rails.logger.info "BackgroundTasks: Failure fetching recordings from #{server.url} #{e.inspect}" + Rails.logger.debug "BackgroundTasks: #{e.backtrace.join("\n")}" + end + end + + def self.update_recordings_for_room(room) + begin + room.fetch_recordings + Rails.logger.info "BackgroundTasks: List of recordings for #{room.meetingid} updated successfully" + rescue StandardError => e + Rails.logger.info "BackgroundTasks: Failure fetching recordings for room #{room.meetingid} #{e.inspect}" + Rails.logger.debug "BackgroundTasks: #{e.backtrace.join("\n")}" end - Rails.logger.info "BackgroundTasks: Ended the update of recordings for all servers" end end end diff --git a/spec/controllers/bigbluebutton/rooms_controller_spec.rb b/spec/controllers/bigbluebutton/rooms_controller_spec.rb index 9fb26c76..67fa2c96 100644 --- a/spec/controllers/bigbluebutton/rooms_controller_spec.rb +++ b/spec/controllers/bigbluebutton/rooms_controller_spec.rb @@ -792,13 +792,16 @@ before do mock_server_and_api end + let(:scope) { + BigbluebuttonRecording.where(room: room, state: BigbluebuttonRecording::STATES.values) + } let(:filter) { - { :meetingID => room.meetingid } + { :meetingID => room.meetingid, :state => BigbluebuttonRecording::STATES.values } } context "on success" do before(:each) { - mocked_server.should_receive(:fetch_recordings).with(filter) + mocked_server.should_receive(:fetch_recordings).with(filter, scope) post :fetch_recordings, :id => room.to_param } it { should respond_with(:redirect) } @@ -808,7 +811,7 @@ context "responds to :json" do before(:each) { - mocked_server.should_receive(:fetch_recordings).with(filter) + mocked_server.should_receive(:fetch_recordings).with(filter, scope) post :fetch_recordings, :id => room.to_param, :format => :json } it { should respond_with(:success) } @@ -840,7 +843,7 @@ context "with :redir_url" do context "on success" do before(:each) { - mocked_server.should_receive(:fetch_recordings).with(filter) + mocked_server.should_receive(:fetch_recordings).with(filter, scope) post :fetch_recordings, :id => room.to_param, :redir_url => "/any" } it {should respond_with(:redirect) } diff --git a/spec/lib/bigbluebutton_rails/background_tasks_spec.rb b/spec/lib/bigbluebutton_rails/background_tasks_spec.rb index 21c8697b..2118dbb2 100644 --- a/spec/lib/bigbluebutton_rails/background_tasks_spec.rb +++ b/spec/lib/bigbluebutton_rails/background_tasks_spec.rb @@ -136,18 +136,109 @@ end end - describe ".update_recordings" do - context "fetches the meetings for all servers" do - let!(:server1) { FactoryGirl.create(:bigbluebutton_server) } - let!(:server2) { FactoryGirl.create(:bigbluebutton_server) } + describe ".update_recordings_by_server" do + let!(:server1) { FactoryGirl.create(:bigbluebutton_server) } + let!(:server2) { FactoryGirl.create(:bigbluebutton_server) } + + context "fetches the recordings for all servers if none is informed" do + before { + BigbluebuttonServer.stub(:find_each).and_yield(server1).and_yield(server2) + server1.should_receive(:fetch_recordings).once.with(no_args) + server2.should_receive(:fetch_recordings).once.with(no_args) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_by_server } + end + + context "fetches the recordings for the server informed" do before { BigbluebuttonServer.stub(:find_each).and_yield(server1).and_yield(server2) - server1.should_receive(:fetch_recordings).once.with(nil, true) - server2.should_receive(:fetch_recordings).once.with(nil, true) + server1.should_receive(:fetch_recordings).once.with(no_args) + server2.should_not_receive(:fetch_recordings) } - it { BigbluebuttonRails::BackgroundTasks.update_recordings } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_by_server(server1) } end - it "doesn't break if exceptions are returned" + context "doesn't break if exceptions happen in one of the requests" do + before { + BigbluebuttonServer.stub(:find_each).and_yield(server1).and_yield(server2) + server1.should_receive(:fetch_recordings).once.with(no_args) { raise IncorrectUrlError.new } + server2.should_receive(:fetch_recordings).once.with(no_args) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_by_server } + end + end + + describe ".update_recordings_by_room" do + let!(:room1) { FactoryGirl.create(:bigbluebutton_room) } + let!(:room2) { FactoryGirl.create(:bigbluebutton_room) } + let!(:room3) { FactoryGirl.create(:bigbluebutton_room) } + + context "fetches the recordings for all rooms if no query is informed" do + before { + BigbluebuttonRoom.stub(:find_each).and_yield(room1).and_yield(room2).and_yield(room3) + room1.should_receive(:fetch_recordings).once.with(no_args) + room2.should_receive(:fetch_recordings).once.with(no_args) + room3.should_receive(:fetch_recordings).once.with(no_args) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_by_room } + end + + context "fetches the recordings for the rooms using the query informed" do + let(:query) { BigbluebuttonRoom.where(id: room2.id) } + before { + BigbluebuttonRoom.stub(:find_each).and_yield(room1).and_yield(room2).and_yield(room3) + query.stub(:find_each).and_yield(room2) + room1.should_not_receive(:fetch_recordings) + room2.should_receive(:fetch_recordings).once.with(no_args) + room3.should_not_receive(:fetch_recordings) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_by_room(query) } + end + + context "doesn't break if exceptions happen in one of the requests" do + before { + BigbluebuttonRoom.stub(:find_each).and_yield(room1).and_yield(room2).and_yield(room3) + room1.should_receive(:fetch_recordings).once.with(no_args) { raise IncorrectUrlError.new } + room2.should_receive(:fetch_recordings).once.with(no_args) + room3.should_receive(:fetch_recordings).once.with(no_args) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_by_room } + end + end + + describe ".update_recordings_for_server" do + let!(:server) { FactoryGirl.create(:bigbluebutton_server) } + + context "fetches the recordings the server passed" do + before { + server.should_receive(:fetch_recordings).once.with(no_args) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_for_server(server) } + end + + context "rescues from exceptions" do + before { + server.should_receive(:fetch_recordings).once.with(no_args) { raise IncorrectUrlError.new } + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_for_server(server) } + end + end + + describe ".update_recordings_for_room" do + let!(:room) { FactoryGirl.create(:bigbluebutton_room) } + + context "fetches the recordings the room passed" do + before { + room.should_receive(:fetch_recordings).once.with(no_args) + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_for_room(room) } + end + + context "rescues from exceptions" do + before { + room.should_receive(:fetch_recordings).once.with(no_args) { raise IncorrectUrlError.new } + } + it { BigbluebuttonRails::BackgroundTasks.update_recordings_for_room(room) } + end end end diff --git a/spec/models/bigbluebutton_recording_spec.rb b/spec/models/bigbluebutton_recording_spec.rb index 90ac3c4b..c2ccdc64 100644 --- a/spec/models/bigbluebutton_recording_spec.rb +++ b/spec/models/bigbluebutton_recording_spec.rb @@ -485,7 +485,7 @@ @r2 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server) @r3 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => FactoryGirl.create(:bigbluebutton_server)) @r4 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => FactoryGirl.create(:bigbluebutton_server)) - BigbluebuttonRecording.sync(new_server, data, true) + BigbluebuttonRecording.sync(new_server, data, BigbluebuttonRecording.where(server: new_server)) } it { BigbluebuttonRecording.count.should == 5 } it ("recording from the target server") { @r1.reload.available.should == false } @@ -499,7 +499,7 @@ new_server.recordings.delete_all @r1 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => FactoryGirl.create(:bigbluebutton_server)) @r2 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => FactoryGirl.create(:bigbluebutton_server)) - BigbluebuttonRecording.sync(new_server, data, true) + BigbluebuttonRecording.sync(new_server, data, BigbluebuttonRecording.where(server: new_server)) } it { new_server.recordings.should be_empty } it ("recording from another server") { @r1.reload.available.should == true } @@ -511,12 +511,28 @@ BigbluebuttonRecording.delete_all @r1 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server) @r2 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server) - BigbluebuttonRecording.sync(new_server, data, true) + BigbluebuttonRecording.sync(new_server, data, BigbluebuttonRecording.where(server: new_server)) } it { BigbluebuttonRecording.count.should == 3 } it ("recording from another server") { @r1.reload.available.should == false } it ("recording from another server") { @r2.reload.available.should == false } end + + context "when updating for a single room" do + let(:target_room) { FactoryGirl.create(:bigbluebutton_room) } + before { + BigbluebuttonRecording.delete_all + @r1 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, :room => target_room) + @r2 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, :room => target_room) + @r3 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, :room => FactoryGirl.create(:bigbluebutton_room)) + @r4 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, :room => FactoryGirl.create(:bigbluebutton_room)) + BigbluebuttonRecording.sync(new_server, data, BigbluebuttonRecording.where(room: target_room)) + } + it ("recording from the target server") { @r1.reload.available.should == false } + it ("recording from the target server") { @r2.reload.available.should == false } + it ("recording from another server") { @r3.reload.available.should == true } + it ("recording from another server") { @r4.reload.available.should == true } + end end context "sets recording that are in the parameters as available in a full sync" do @@ -524,12 +540,12 @@ BigbluebuttonRecording.delete_all @r = FactoryGirl.create(:bigbluebutton_recording, :available => false, :server => new_server, :recordid => data[0][:recordID]) # so it creates the recording the first time - BigbluebuttonRecording.sync(new_server, data, true) + BigbluebuttonRecording.sync(new_server, data, BigbluebuttonRecording.where(server: new_server)) BigbluebuttonRecording.find_by(recordid: data[0][:recordID]) .update_attributes(available: false) - BigbluebuttonRecording.sync(new_server, data, true) + BigbluebuttonRecording.sync(new_server, data, BigbluebuttonRecording.where(server: new_server)) } it { @r.reload.available.should be(true) } end diff --git a/spec/models/bigbluebutton_room_spec.rb b/spec/models/bigbluebutton_room_spec.rb index 465e1629..cd4df928 100644 --- a/spec/models/bigbluebutton_room_spec.rb +++ b/spec/models/bigbluebutton_room_spec.rb @@ -1516,6 +1516,33 @@ it { room.send(:internal_create_meeting, nil, user_opts) } end end + + describe "#fetch_recordings" do + let!(:server) { FactoryGirl.create(:bigbluebutton_server) } + let!(:room) { FactoryGirl.create(:bigbluebutton_room) } + + it { should respond_to(:fetch_recordings) } + + context "if no server is found" do + before { + room.stub(:select_server).and_return(nil) + server.should_not_receive(:fetch_recordings) + } + + it { room.fetch_recordings.should be(false) } + end + + context "if a server is found" do + before { + room.stub(:select_server).and_return(server) + filter = { meetingID: room.meetingid, state: BigbluebuttonRecording::STATES.values } + scope = BigbluebuttonRecording.where(room: room, state: BigbluebuttonRecording::STATES.values) + server.should_receive(:fetch_recordings).with(filter, scope) + } + + it { room.fetch_recordings.should be(true) } + end + end end def get_create_params(room, user=nil) diff --git a/spec/models/bigbluebutton_server_spec.rb b/spec/models/bigbluebutton_server_spec.rb index 2ecfd689..51061e82 100644 --- a/spec/models/bigbluebutton_server_spec.rb +++ b/spec/models/bigbluebutton_server_spec.rb @@ -251,7 +251,8 @@ describe "#fetch_recordings" do let(:server) { FactoryGirl.create(:bigbluebutton_server) } - let(:params) { { :meetingID => "id1,id2,id3" } } + let(:filter) { { :meetingID => "id1,id2,id3" } } + let(:response) { { :recordings => [1, 2] } } before do @api_mock = double(BigBlueButton::BigBlueButtonApi) server.stub(:api).and_return(@api_mock) @@ -259,56 +260,47 @@ it { should respond_to(:fetch_recordings) } - context "calls get_recordings" do - let(:response) { { :recordings => [1, 2] } } + context "calls get_recordings and sync" do + let(:expected_scope) { BigbluebuttonRecording.where(server: server) } before do - @api_mock.should_receive(:get_recordings).with(params).and_return(response) - BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], false) + @api_mock.should_receive(:get_recordings).with({}).and_return(response) + BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], expected_scope) end - it { server.fetch_recordings(params) } + it { server.fetch_recordings } end - context "calls get_recordings when `full_sync` is set" do - let(:response) { { :recordings => [1, 2] } } + context "when only a filter is informed, calls get_recordings with the filter received" do before do - @api_mock.should_receive(:get_recordings).with(params).and_return(response) - BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], true) + @api_mock.should_receive(:get_recordings).with(filter).and_return(response) + BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], nil) end - it { server.fetch_recordings(params, true) } + it { server.fetch_recordings(filter) } end - context "calls get_recordings when `filters` is not set" do - let(:response) { { :recordings => [1, 2] } } + context "when only a scope is informed, calls sync with the scope received" do + let(:scope) { BigbluebuttonRecording.where(id: 1) } before do @api_mock.should_receive(:get_recordings).with({}).and_return(response) - BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], false) + BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], scope) end - it { server.fetch_recordings } + it { server.fetch_recordings(nil, scope) } end - context "when the response is empty" do + context "when the response of get_recordings is empty doesn't call sync" do let(:response) { { :recordings => [1, 2] } } before do - @api_mock.should_receive(:get_recordings).with(params).and_return(nil) + @api_mock.should_receive(:get_recordings).with(filter).and_return(nil) BigbluebuttonRecording.should_not_receive(:sync) end - it { server.fetch_recordings(params) } + it { server.fetch_recordings(filter) } end - context "when the response has no :recordings element" do + context "when the response of get_recordings has no :recordings element doesn't call sync" do before do - @api_mock.should_receive(:get_recordings).with(params).and_return({}) + @api_mock.should_receive(:get_recordings).with(filter).and_return({}) BigbluebuttonRecording.should_not_receive(:sync) end - it { server.fetch_recordings(params) } - end - - context "works without parameters" do - before do - @api_mock.should_receive(:get_recordings).with({}).and_return(nil) - BigbluebuttonRecording.should_not_receive(:sync) - end - it { server.fetch_recordings } + it { server.fetch_recordings(filter) } end end diff --git a/spec/workers/bigbluebutton_update_recordings_worker_spec.rb b/spec/workers/bigbluebutton_update_recordings_worker_spec.rb index 55b5d043..0b9cc039 100644 --- a/spec/workers/bigbluebutton_update_recordings_worker_spec.rb +++ b/spec/workers/bigbluebutton_update_recordings_worker_spec.rb @@ -3,7 +3,7 @@ describe BigbluebuttonUpdateRecordingsWorker do it "runs BigbluebuttonRails::BackgroundTasks.finish_meetings" do - expect(BigbluebuttonRails::BackgroundTasks).to receive(:update_recordings).once + expect(BigbluebuttonRails::BackgroundTasks).to receive(:update_recordings_by_room).once BigbluebuttonUpdateRecordingsWorker.perform end From 2fe17606d775e314bb17f197ce25483753a6f2fe Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Tue, 25 May 2021 15:10:28 -0300 Subject: [PATCH 07/15] Sync recordings only for rooms that had meetings in the past 7 days Instead of going through all rooms to fetch their recordings, do it only for rooms that had meetings in the past 7 days. This logic can be easily changed by the application using the gem by overriding the method BigbluebuttonRails.configuration.rooms_for_full_recording_sync --- .../bigbluebutton_update_recordings_worker.rb | 4 +- lib/bigbluebutton_rails/configuration.rb | 13 +++++ .../bigbluebutton_rails/configuration_spec.rb | 50 +++++++++++++++++++ ...luebutton_update_recordings_worker_spec.rb | 13 +++-- 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 spec/lib/bigbluebutton_rails/configuration_spec.rb diff --git a/app/workers/bigbluebutton_update_recordings_worker.rb b/app/workers/bigbluebutton_update_recordings_worker.rb index 36725779..ad864138 100644 --- a/app/workers/bigbluebutton_update_recordings_worker.rb +++ b/app/workers/bigbluebutton_update_recordings_worker.rb @@ -7,8 +7,8 @@ class BigbluebuttonUpdateRecordingsWorker def self.perform(server_id=nil) Rails.logger.info "BigbluebuttonUpdateRecordingsWorker worker running" - # TODO: configurable filter of rooms - BigbluebuttonRails::BackgroundTasks.update_recordings_by_room + query = BigbluebuttonRails.configuration.rooms_for_full_recording_sync.call + BigbluebuttonRails::BackgroundTasks.update_recordings_by_room(query) Rails.logger.info "BigbluebuttonUpdateRecordingsWorker worker ended" end diff --git a/lib/bigbluebutton_rails/configuration.rb b/lib/bigbluebutton_rails/configuration.rb index 2ad74fb4..308788a3 100644 --- a/lib/bigbluebutton_rails/configuration.rb +++ b/lib/bigbluebutton_rails/configuration.rb @@ -25,6 +25,7 @@ class Configuration attr_accessor :get_invitation_url attr_accessor :get_create_options attr_accessor :get_join_options + attr_accessor :rooms_for_full_recording_sync def initialize @controllers = { @@ -116,6 +117,18 @@ def initialize 2.hours, 2.hours, 3.hours, 3.hours, 3.hours, 3.hours, 3.hours, 3.hours ] + + # Return a query that selects the rooms that should be iterated over when updating all + # recordings in a full sync. This is used so users can program a logic to select only + # a subset of rooms in case there are too many rooms to iterate over every time. + # Return `nil` to iterate over all rooms. + @rooms_for_full_recording_sync = Proc.new do + # Get only the rooms that had at least one meeting in the previous 7 days + since = (DateTime.now - 7.days).to_i * 1000 # create_time is a 13-digit timestamp + room_ids = BigbluebuttonMeeting.where("create_time >= ? ", since) + .select(:room_id).distinct.pluck(:room_id) + BigbluebuttonRoom.where(id: room_ids) + end end def set_controllers(options) diff --git a/spec/lib/bigbluebutton_rails/configuration_spec.rb b/spec/lib/bigbluebutton_rails/configuration_spec.rb new file mode 100644 index 00000000..bb0e22de --- /dev/null +++ b/spec/lib/bigbluebutton_rails/configuration_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe BigbluebuttonRails::Configuration do + + describe "#rooms_for_full_recording_sync" do + let(:config) { BigbluebuttonRails::Configuration.new } + + it { expect(config.rooms_for_full_recording_sync).to be_a(Proc) } + + context "returns a query to find rooms that had meetings in the past 7 days" do + let!(:room1) { FactoryGirl.create(:bigbluebutton_room) } + let!(:meeting1) { + create_time = DateTime.now.to_i * 1000 + FactoryGirl.create(:bigbluebutton_meeting, room: room1, create_time: create_time) + } + + let!(:room2) { FactoryGirl.create(:bigbluebutton_room) } + let!(:meeting2) { + create_time = (DateTime.now - 1.day).to_i * 1000 + FactoryGirl.create(:bigbluebutton_meeting, room: room2, create_time: create_time) + } + + let!(:room3) { FactoryGirl.create(:bigbluebutton_room) } + let!(:meeting3) { + create_time = (DateTime.now - 7.days).to_i * 1000 + FactoryGirl.create(:bigbluebutton_meeting, room: room3, create_time: create_time) + } + + let!(:room4) { FactoryGirl.create(:bigbluebutton_room) } + let!(:meeting4) { + create_time = (DateTime.now - 7.days - 1.second).to_i * 1000 + FactoryGirl.create(:bigbluebutton_meeting, room: room4, create_time: create_time) + } + + let!(:room5) { FactoryGirl.create(:bigbluebutton_room) } + let!(:meeting5) { + create_time = (DateTime.now - 8.days).to_i * 1000 + FactoryGirl.create(:bigbluebutton_meeting, room: room5, create_time: create_time) + } + + let(:expected) { BigbluebuttonRoom.where(id: [room1.id, room2.id, room3.id]) } + + before { Timecop.freeze } + after { Timecop.return } + + it { expect(config.rooms_for_full_recording_sync.call).to be_a(ActiveRecord::Relation) } + it { expect(config.rooms_for_full_recording_sync.call.to_sql.should eql(expected.to_sql)) } + end + end +end diff --git a/spec/workers/bigbluebutton_update_recordings_worker_spec.rb b/spec/workers/bigbluebutton_update_recordings_worker_spec.rb index 0b9cc039..c00a8b4e 100644 --- a/spec/workers/bigbluebutton_update_recordings_worker_spec.rb +++ b/spec/workers/bigbluebutton_update_recordings_worker_spec.rb @@ -2,9 +2,16 @@ describe BigbluebuttonUpdateRecordingsWorker do - it "runs BigbluebuttonRails::BackgroundTasks.finish_meetings" do - expect(BigbluebuttonRails::BackgroundTasks).to receive(:update_recordings_by_room).once - BigbluebuttonUpdateRecordingsWorker.perform + context "runs BigbluebuttonRails::BackgroundTasks.finish_meetings" do + let(:query) { BigbluebuttonRoom.where(id: 1) } + let(:proc) { Proc.new {} } + + before { + expect(proc).to receive(:call).once.and_return(query) + expect(BigbluebuttonRails.configuration).to receive(:rooms_for_full_recording_sync).once.and_return(proc) + expect(BigbluebuttonRails::BackgroundTasks).to receive(:update_recordings_by_room).once.with(query) + } + it { BigbluebuttonUpdateRecordingsWorker.perform } end it "uses the queue :bigbluebutton_rails" do From d7e3a18bdbb816a73dc2b97673bbc98495d3b15c Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Tue, 25 May 2021 18:32:04 -0300 Subject: [PATCH 08/15] Update how we avoid setting the wrong recs as unavailable It's a merge and adaptation of PR#195 to the code of this branch. --- app/models/bigbluebutton_recording.rb | 21 +++++++++++++++---- app/models/bigbluebutton_server.rb | 5 ++++- spec/models/bigbluebutton_recording_spec.rb | 23 +++++++++++++++++++-- spec/models/bigbluebutton_server_spec.rb | 11 +++++++--- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/app/models/bigbluebutton_recording.rb b/app/models/bigbluebutton_recording.rb index ee66bcfb..be85d26d 100644 --- a/app/models/bigbluebutton_recording.rb +++ b/app/models/bigbluebutton_recording.rb @@ -153,15 +153,23 @@ def self.recording_changed?(recording, data) # Will add new recordings that are not in the db yet and update the ones that # already are (matching by 'recordid'). Will NOT delete recordings from the db # if they are not in the array but instead mark them as unavailable. - # 'server' is the BigbluebuttonServer object from which the recordings - # were fetched. + # + # server:: The BigbluebuttonServer from which the recordings were fetched. + # recordings:: The response from getRecordings. + # sync_scope:: The scope to which these recordings are part of. If we fetched all recordings + # in a server, the scope is all recordings in the server; if we fetched only recordings + # for a room, the scope is the recordings of this room. This is used to set `available` + # in the recordings that might not be in the server anymore. + # sync_started_at:: Moment when the getRecordings call that returned the `recordings` + # was made. Used so we don't set `available` on recordings created during the + # synchronization process. # # TODO: catch exceptions on creating/updating recordings - def self.sync(server, recordings, sync_scope=nil) + def self.sync(server, recordings, sync_scope=nil, sync_started_at=nil) logger.info "Sync recordings: starting a sync for server=#{server.url};#{server.secret} sync_scope=\"#{sync_scope&.to_sql}\"" recordings.each do |rec| - rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID]) + rec_obj = BigbluebuttonRecording.find_by(recordid: rec[:recordID]) rec_data = adapt_recording_hash(rec) changed = !rec_obj.present? || self.recording_changed?(rec_obj, rec_data) @@ -186,20 +194,25 @@ def self.sync(server, recordings, sync_scope=nil) # only if there is a scope set, otherwise we don't know how the call to getRecordings was filtered. # Uses the scope passed to figure out which recordings should be marked as unavailable. if sync_scope.present? + sync_started_at = DateTime.now if sync_started_at.nil? + recordIDs = recordings.map{ |rec| rec[:recordID] } if recordIDs.length <= 0 # empty response, all recordings in the scope are unavailable sync_scope. where(available: true). + where("created_at <= ?", sync_started_at). update_all(available: false) else # non empty response, mark as unavailable all recordings in the scope that # were not returned by getRecording sync_scope. where(available: true).where.not(recordid: recordIDs). + where("created_at <= ?", sync_started_at). update_all(available: false) sync_scope. where(available: false, recordid: recordIDs). + where("created_at <= ?", sync_started_at). update_all(available: true) end end diff --git a/app/models/bigbluebutton_server.rb b/app/models/bigbluebutton_server.rb index 69983d99..da23e22b 100644 --- a/app/models/bigbluebutton_server.rb +++ b/app/models/bigbluebutton_server.rb @@ -137,14 +137,17 @@ def send_delete_recordings(ids) def fetch_recordings(filter=nil, sync_scope=nil) filter ||= {} logger.info "Fetching recordings on #{self.url} with filter: #{filter.inspect}" + + sync_started_at = DateTime.now recordings = self.api.get_recordings(filter) + if recordings and recordings[:recordings] # if no scope is set and there are no filters, set the scope to all recordings # in this server sync_scope = BigbluebuttonRecording.where(server: self) if filter.blank? && sync_scope.nil? - BigbluebuttonRecording.sync(self, recordings[:recordings], sync_scope) + BigbluebuttonRecording.sync(self, recordings[:recordings], sync_scope, sync_started_at) end end diff --git a/spec/models/bigbluebutton_recording_spec.rb b/spec/models/bigbluebutton_recording_spec.rb index c2ccdc64..a204a696 100644 --- a/spec/models/bigbluebutton_recording_spec.rb +++ b/spec/models/bigbluebutton_recording_spec.rb @@ -476,8 +476,7 @@ it { BigbluebuttonRecording.count.should == 2 } end - context "sets recording that are not in the parameters as unavailable in a full sync" do - + context "sets recording that are not in the parameters as unavailable" do context "for recordings in multiple servers" do before { BigbluebuttonRecording.delete_all @@ -533,6 +532,26 @@ it ("recording from another server") { @r3.reload.available.should == true } it ("recording from another server") { @r4.reload.available.should == true } end + + context "only changes 'available' for recordings created before the sync started" do + before { + sync_started_at = DateTime.now + + BigbluebuttonRecording.delete_all + @r1 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, created_at: sync_started_at + 1.hour) + @r2 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, created_at: sync_started_at + 1.second) + @r3 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, created_at: sync_started_at) + @r4 = FactoryGirl.create(:bigbluebutton_recording, :available => true, :server => new_server, created_at: sync_started_at - 1.second) + + scope = BigbluebuttonRecording.where(server: new_server) + BigbluebuttonRecording.sync(new_server, data, scope, sync_started_at) + } + it { BigbluebuttonRecording.count.should == 5 } + it ("recording created after the sync started") { @r1.reload.available.should == true } + it ("recording created after the sync started") { @r2.reload.available.should == true } + it ("recording created before the sync started") { @r3.reload.available.should == false } + it ("recording created before the sync started") { @r4.reload.available.should == false } + end end context "sets recording that are in the parameters as available in a full sync" do diff --git a/spec/models/bigbluebutton_server_spec.rb b/spec/models/bigbluebutton_server_spec.rb index 51061e82..07098d18 100644 --- a/spec/models/bigbluebutton_server_spec.rb +++ b/spec/models/bigbluebutton_server_spec.rb @@ -253,6 +253,8 @@ let(:server) { FactoryGirl.create(:bigbluebutton_server) } let(:filter) { { :meetingID => "id1,id2,id3" } } let(:response) { { :recordings => [1, 2] } } + let!(:sync_started_at) { DateTime.now } + before do @api_mock = double(BigBlueButton::BigBlueButtonApi) server.stub(:api).and_return(@api_mock) @@ -263,16 +265,18 @@ context "calls get_recordings and sync" do let(:expected_scope) { BigbluebuttonRecording.where(server: server) } before do + DateTime.should_receive(:now).once.and_return(sync_started_at) @api_mock.should_receive(:get_recordings).with({}).and_return(response) - BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], expected_scope) + BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], expected_scope, sync_started_at) end it { server.fetch_recordings } end context "when only a filter is informed, calls get_recordings with the filter received" do before do + DateTime.should_receive(:now).once.and_return(sync_started_at) @api_mock.should_receive(:get_recordings).with(filter).and_return(response) - BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], nil) + BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], nil, sync_started_at) end it { server.fetch_recordings(filter) } end @@ -280,8 +284,9 @@ context "when only a scope is informed, calls sync with the scope received" do let(:scope) { BigbluebuttonRecording.where(id: 1) } before do + DateTime.should_receive(:now).once.and_return(sync_started_at) @api_mock.should_receive(:get_recordings).with({}).and_return(response) - BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], scope) + BigbluebuttonRecording.should_receive(:sync).with(server, response[:recordings], scope, sync_started_at) end it { server.fetch_recordings(nil, scope) } end From e310a1dccc355ae2a7df3bae78fdd301b8fdff26 Mon Sep 17 00:00:00 2001 From: Leonardo Crauss Daronco Date: Tue, 25 May 2021 22:15:46 -0300 Subject: [PATCH 09/15] Change the schedule of update_recordings to run less It is not as necessary anymore, the worker that monitors recordings after a meeting ends is usually enough to detect recordings. --- config/resque/workers_schedule.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/resque/workers_schedule.yml b/config/resque/workers_schedule.yml index ad242f4e..e660ed3c 100644 --- a/config/resque/workers_schedule.yml +++ b/config/resque/workers_schedule.yml @@ -5,7 +5,6 @@ finish_meetings: description: "Checks for meetings that finished and mark as finished. Same as 'rake bigbluebutton_rails:meetings:finish'." update_recordings: - every: - - "30m" + cron: "0 0,12 * * *" # twice a day class: BigbluebuttonUpdateRecordingsWorker description: "Gets the recordings in the server to populate the db. Same as 'rake bigbluebutton_rails:recordings:update'." From 6bb52ddd938228962bdedfaee1e49bae52111633 Mon Sep 17 00:00:00 2001 From: JoaoCarAlmSilva Date: Thu, 27 May 2021 18:04:46 -0300 Subject: [PATCH 10/15] Fix tries on worker --- app/workers/bigbluebutton_recordings_for_room_worker.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/workers/bigbluebutton_recordings_for_room_worker.rb b/app/workers/bigbluebutton_recordings_for_room_worker.rb index 6f1d6c49..71a56a0b 100644 --- a/app/workers/bigbluebutton_recordings_for_room_worker.rb +++ b/app/workers/bigbluebutton_recordings_for_room_worker.rb @@ -25,6 +25,7 @@ def self.perform(room_id, tries_left=0) intervals = BigbluebuttonRails.configuration.recording_sync_for_room_intervals idx = intervals.length - tries_left + wait = intervals[idx] wait = intervals[intervals.length - 1] if wait.nil? Resque.enqueue_in(wait, ::BigbluebuttonRecordingsForRoomWorker, room_id, tries_left - 1) From e17afe24e60f26ebb6f8e56286b530aba302972b Mon Sep 17 00:00:00 2001 From: gscariott Date: Thu, 10 Jun 2021 15:08:24 -0300 Subject: [PATCH 11/15] Fix meeting actions The gem was forcing the meeting's actions on their apps (like Elos' portal). Now they can be overriden if necessary. --- lib/bigbluebutton_rails/configuration.rb | 5 +++-- lib/bigbluebutton_rails/rails/routes.rb | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/bigbluebutton_rails/configuration.rb b/lib/bigbluebutton_rails/configuration.rb index 308788a3..d50e60c7 100644 --- a/lib/bigbluebutton_rails/configuration.rb +++ b/lib/bigbluebutton_rails/configuration.rb @@ -32,7 +32,8 @@ def initialize servers: 'bigbluebutton/servers', rooms: 'bigbluebutton/rooms', recordings: 'bigbluebutton/recordings', - playback_types: 'bigbluebutton/playback_types' + playback_types: 'bigbluebutton/playback_types', + meetings: 'bigbluebutton/meetings' } @routing_scope = 'bigbluebutton' @@ -133,7 +134,7 @@ def initialize def set_controllers(options) unless options.nil? || options.empty? - @controllers.merge!(options).slice!(:servers, :rooms, :recordings, :playback_types) + @controllers.merge!(options).slice!(:servers, :rooms, :recordings, :playback_types, :meetings) end end diff --git a/lib/bigbluebutton_rails/rails/routes.rb b/lib/bigbluebutton_rails/rails/routes.rb index 21c6d241..acb77e0b 100644 --- a/lib/bigbluebutton_rails/rails/routes.rb +++ b/lib/bigbluebutton_rails/rails/routes.rb @@ -162,7 +162,8 @@ def add_routes_for_playback_types #:nodoc: end def add_routes_for_meetings #:nodoc: - resources :meetings, :controller => 'bigbluebutton/meetings', :only => [:destroy, :update, :edit] + resources :meetings, :only => [:destroy, :update, :edit], + :controller => BigbluebuttonRails.configuration.controllers[:meetings] end end end From d381d2ac8cfad9918a8f1cb5020d6f7665baf2ec Mon Sep 17 00:00:00 2001 From: wpramio Date: Tue, 15 Jun 2021 15:12:33 -0300 Subject: [PATCH 12/15] Rescue BBBException when deleting recordings from server - Also, specs to check if both a meeting and its associated recording are destroyed --- app/models/bigbluebutton_recording.rb | 7 ++++- spec/models/bigbluebutton_meeting_spec.rb | 32 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/app/models/bigbluebutton_recording.rb b/app/models/bigbluebutton_recording.rb index be85d26d..e3546e64 100644 --- a/app/models/bigbluebutton_recording.rb +++ b/app/models/bigbluebutton_recording.rb @@ -72,7 +72,12 @@ def default_playback_format # Remove this recording from the server def delete_from_server! if self.server.present? - self.server.send_delete_recordings(self.recordid) + begin + self.server.send_delete_recordings(self.recordid) + rescue BigBlueButton::BigBlueButtonException => e + logger.error "Could not delete the recording #{self.id} from the server. API error: #{e}" + return false + end else false end diff --git a/spec/models/bigbluebutton_meeting_spec.rb b/spec/models/bigbluebutton_meeting_spec.rb index 5e3d438a..1e342e47 100644 --- a/spec/models/bigbluebutton_meeting_spec.rb +++ b/spec/models/bigbluebutton_meeting_spec.rb @@ -13,6 +13,38 @@ it { should have_one(:recording).dependent(:destroy) } + describe "recording association" do + let!(:meeting) { FactoryGirl.create(:bigbluebutton_meeting, ended: true) } + let!(:recording) { FactoryGirl.create(:bigbluebutton_recording, meeting: meeting) } + + context "when the meeting is successfully destroyed" do + before do + expect(BigbluebuttonMeeting.where(id: meeting.id).count).to eq(1) + expect(BigbluebuttonRecording.where(meeting_id: meeting.id).count).to eq(1) + BigbluebuttonServer.any_instance.stub(:send_delete_recordings).and_return(true) + end + + it "should destroy the meeting and the associated recording" do + meeting.destroy + expect(BigbluebuttonMeeting.where(id: meeting.id).count).to eq(0) + expect(BigbluebuttonRecording.where(meeting_id: meeting.id).count).to eq(0) + end + end + + context "when the meeting fails to be destroyed" do + before do + expect(BigbluebuttonMeeting.where(id: meeting.id).count).to eq(1) + expect(BigbluebuttonRecording.where(meeting_id: meeting.id).count).to eq(1) + BigbluebuttonServer.any_instance.stub(:send_delete_recordings).and_return(false) + end + it "should not destroy the meeting nor the associated recording" do + meeting.destroy + expect(BigbluebuttonMeeting.where(id: meeting.id).count).to eq(1) + expect(BigbluebuttonRecording.where(meeting_id: meeting.id).count).to eq(1) + end + end + end + it { should validate_presence_of(:meetingid) } it { should ensure_length_of(:meetingid).is_at_least(1).is_at_most(100) } From 6673d10066263d6ac5c346302310a1667ecd9074 Mon Sep 17 00:00:00 2001 From: JoaoCarAlmSilva Date: Tue, 15 Jun 2021 17:38:26 -0300 Subject: [PATCH 13/15] Version 3.2.0 --- CHANGELOG.md | 15 +++++++++++++++ Gemfile.lock | 2 +- lib/bigbluebutton_rails/version.rb | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f98e7ae..594d1b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## [3.2.0] - 2021-06-15 +* [#198] Fix deletion of meetings with recordings. + When deleting a meeting with recording, + sometimes errors occurred and the recording was kept. + Now these errors are rescued and both are deleted. +* [#197] Now the meetings controller can be overriden on apps if necessary. +* [#196] Fix race condition of workers and improve performance. + Now workers will run sync for each rooms, not for each server, and + will run 16 times for about 24h after a meeting end, + applications can easily customize the intervals between each job +* Migration to: + - Add missing `BigbluebuttonRecording#state` attribute + + ## [3.1.2] - 2021-05-15 * [#190] Increase the size of `BigbluebuttonMeeting#title` from 80 to 255 characters. It was a column @@ -379,6 +393,7 @@ https://github.com/mconf/bigbluebutton_rails/wiki/Migrate-to-1.3.0 * Controller to access servers and rooms * rooms_controller interacts with a BBB server using bigbluebutton-api-ruby +[3.2.0]: https://github.com/mconf/bigbluebutton_rails/compare/v3.1.2...v3.2.0 [3.1.2]: https://github.com/mconf/bigbluebutton_rails/compare/v3.1.1...v3.1.2 [3.1.1]: https://github.com/mconf/bigbluebutton_rails/compare/v3.1.0...v3.1.1 [3.1.0]: https://github.com/mconf/bigbluebutton_rails/compare/v3.0.1...v3.1.0 diff --git a/Gemfile.lock b/Gemfile.lock index 278e819e..4836afc3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - bigbluebutton_rails (3.1.2) + bigbluebutton_rails (3.2.0) activerecord-import (~> 1.0) bigbluebutton-api-ruby (~> 1.6) browser (~> 0.8.0) diff --git a/lib/bigbluebutton_rails/version.rb b/lib/bigbluebutton_rails/version.rb index eba129e4..ce529388 100644 --- a/lib/bigbluebutton_rails/version.rb +++ b/lib/bigbluebutton_rails/version.rb @@ -1,3 +1,3 @@ module BigbluebuttonRails - VERSION = "3.1.2".freeze + VERSION = "3.2.0".freeze end From df9c0b9e08b56a97012a77fa32bb3fa30bb091a8 Mon Sep 17 00:00:00 2001 From: JoaoCarAlmSilva Date: Thu, 17 Jun 2021 13:56:00 -0300 Subject: [PATCH 14/15] Fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 594d1b8f..3b7703b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Now workers will run sync for each rooms, not for each server, and will run 16 times for about 24h after a meeting end, applications can easily customize the intervals between each job -* Migration to: +* Fix migration.rb: - Add missing `BigbluebuttonRecording#state` attribute From 60e7dc18d68ab9dfc4b75b1d09e2075e640f79bf Mon Sep 17 00:00:00 2001 From: wpramio Date: Thu, 1 Jul 2021 21:57:51 -0300 Subject: [PATCH 15/15] Fix specs and some controllers - Meetings controller - Recordings controller - Locales and specs for them - Spec for BigbluebuttonRecordingsForRoomWorker --- .../bigbluebutton/meetings_controller.rb | 2 +- .../bigbluebutton/recordings_controller.rb | 20 +++++-------------- config/locales/en.yml | 4 +++- config/locales/pt-br.yml | 4 +++- .../bigbluebutton/meetings_controller_spec.rb | 4 ++-- .../recordings_controller_spec.rb | 12 ++++++++--- ...ebutton_recordings_for_room_worker_spec.rb | 5 +++-- 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/app/controllers/bigbluebutton/meetings_controller.rb b/app/controllers/bigbluebutton/meetings_controller.rb index 51124eb7..a2f4fab2 100644 --- a/app/controllers/bigbluebutton/meetings_controller.rb +++ b/app/controllers/bigbluebutton/meetings_controller.rb @@ -24,7 +24,7 @@ def destroy } end else - flash[:error] = t('bigbluebutton_rails.meetings.notice.destroy.error_destroy') + flash[:error] = t('bigbluebutton_rails.meetings.notice.destroy.error') redirect_to_using_params_or_back(request.referer) end else diff --git a/app/controllers/bigbluebutton/recordings_controller.rb b/app/controllers/bigbluebutton/recordings_controller.rb index eed66d98..22e7c28c 100644 --- a/app/controllers/bigbluebutton/recordings_controller.rb +++ b/app/controllers/bigbluebutton/recordings_controller.rb @@ -45,24 +45,14 @@ def update end def destroy - error = false - begin - @recording.destroy - message = t('bigbluebutton_rails.recordings.notice.destroy.success') - rescue BigBlueButton::BigBlueButtonException => e - error = true - message = t('bigbluebutton_rails.recordings.notice.destroy.success_with_bbb_error') + if @recording.destroy + flash[:success] = t('bigbluebutton_rails.recordings.notice.destroy.success') + else + flash[:error] = t('bigbluebutton_rails.recordings.notice.destroy.error') end respond_with do |format| - format.html { - if error - flash[:error] = message - redirect_to_using_params bigbluebutton_recordings_url - else - redirect_to_using_params bigbluebutton_recordings_url, :notice => message - end - } + format.html { redirect_to_using_params bigbluebutton_recordings_url } end end diff --git a/config/locales/en.yml b/config/locales/en.yml index d3690f61..1d3d6795 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -93,7 +93,7 @@ en: failure: "It was not possible to update your meeting record." destroy: success_with_bbb_error: "The record was successfully destroyed but it wasn't deleted from the videoconference server (\"%{error}\")" - error_destroy: "This record could not be deleted" + error: "This record could not be deleted" running: not_ended: "This record can not be deleted because the meeting is still running" metadata: @@ -126,6 +126,7 @@ en: destroy: success: "Recording destroyed!" success_with_bbb_error: "The recording was successfully destroyed but it wasn't deleted from the webconference server." + error: "This recording could not be deleted." publish: success: "Recording published!" unpublish: @@ -155,6 +156,7 @@ en: destroy: success: "Room destroyed." success_with_bbb_error: "The room was successfully destroyed but the meeting wasn't ended in the webconference server." + error: "This room could not be deleted." end: not_running: "The meeting could not be ended because it is not running." success: "The meeting was successfully ended." diff --git a/config/locales/pt-br.yml b/config/locales/pt-br.yml index 996a5bb4..c915d92a 100644 --- a/config/locales/pt-br.yml +++ b/config/locales/pt-br.yml @@ -76,7 +76,7 @@ pt-br: failure: "Não foi possível atualizar seu registro de reunião." destroy: success_with_bbb_error: "O registro foi destruído com sucesso mas não foi removido do servidor de videoconferência (\"%{error}\")" - error_destroy: "Não foi possível deletar este registro." + error: "Não foi possível deletar este registro." running: not_ended: "Este registro não pode ser deletado pois a reunião ainda está acontecendo." metadata: @@ -109,6 +109,7 @@ pt-br: destroy: success: "Gravação destruida!" success_with_bbb_error: "A gravação foi destruída com sucesso mas não foi removida do servidor de webconferência." + error: "Não foi possível deletar esta gravação." publish: success: "Gravação publicada!" unpublish: @@ -138,6 +139,7 @@ pt-br: destroy: success: "Reunião destruída!" success_with_bbb_error: "A sala foi destruída com sucesso mas a reunião não foi finalizada no servidor de webconferência." + error: "Não foi possível deletar esta sala." end: not_running: "A reunião não pôde ser finalizada pois não está em andamento." success: "A reunião foi finalizada com sucesso." diff --git a/spec/controllers/bigbluebutton/meetings_controller_spec.rb b/spec/controllers/bigbluebutton/meetings_controller_spec.rb index 5c707c2e..ec5053c2 100644 --- a/spec/controllers/bigbluebutton/meetings_controller_spec.rb +++ b/spec/controllers/bigbluebutton/meetings_controller_spec.rb @@ -38,7 +38,7 @@ } it("should not decrease meetings count") { } it { should redirect_to '/any' } - it { should set_the_flash.to(I18n.t('bigbluebutton_rails.meetings.notice.destroy.error_destroy')) } + it { should set_the_flash.to(I18n.t('bigbluebutton_rails.meetings.notice.destroy.error')) } end end context "when meeting has recordings" do @@ -67,7 +67,7 @@ } it("should not decrease Recordings count") { } it { should redirect_to '/any' } - it { should set_the_flash.to(I18n.t('bigbluebutton_rails.meetings.notice.destroy.error_destroy')) } + it { should set_the_flash.to(I18n.t('bigbluebutton_rails.meetings.notice.destroy.error')) } end end end diff --git a/spec/controllers/bigbluebutton/recordings_controller_spec.rb b/spec/controllers/bigbluebutton/recordings_controller_spec.rb index 19323d06..acfa0a5e 100644 --- a/spec/controllers/bigbluebutton/recordings_controller_spec.rb +++ b/spec/controllers/bigbluebutton/recordings_controller_spec.rb @@ -156,7 +156,10 @@ } it { should respond_with(:redirect) } it { should redirect_to bigbluebutton_recordings_url } - it { should set_the_flash.to(I18n.t('bigbluebutton_rails.recordings.notice.destroy.success')) } + it { + msg = I18n.t('bigbluebutton_rails.recordings.notice.destroy.success') + should set_the_flash.to(msg) + } end context "on failure" do @@ -171,7 +174,7 @@ it { should respond_with(:redirect) } it { should redirect_to bigbluebutton_recordings_url } it { - msg = I18n.t('bigbluebutton_rails.recordings.notice.destroy.success_with_bbb_error') + msg = I18n.t('bigbluebutton_rails.recordings.notice.destroy.error') should set_the_flash.to(msg) } end @@ -207,7 +210,10 @@ } it { should respond_with(:redirect) } it { should redirect_to bigbluebutton_recordings_url } - it { should set_the_flash.to(I18n.t('bigbluebutton_rails.recordings.notice.destroy.success')) } + it { + msg = I18n.t('bigbluebutton_rails.recordings.notice.destroy.error') + should set_the_flash.to(msg) + } end context "doesn't override @recording" do diff --git a/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb b/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb index 877f02fd..3ae9187c 100644 --- a/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb +++ b/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb @@ -18,17 +18,18 @@ end context "if there are still tries left" do + let(:tries_left) { 7 } before { BigbluebuttonRoom.stub(:find).and_return(room) room.stub(:fetch_recordings) intervals = BigbluebuttonRails.configuration.recording_sync_for_room_intervals - wait = intervals[intervals.length - 6] + wait = intervals[intervals.length - tries_left] expect(Resque).to receive(:enqueue_in) .with(wait, ::BigbluebuttonRecordingsForRoomWorker, room.id, 6) .once } - it { BigbluebuttonRecordingsForRoomWorker.perform(room.id, 7) } + it { BigbluebuttonRecordingsForRoomWorker.perform(room.id, tries_left) } end context "if there are no more tries left" do