Merge branch 'refactor-meetings-api' of github.com:oswaldoacauan/bigbluebutton into test-meetings-api

This commit is contained in:
Anton Georgiev 2016-11-07 16:52:51 +00:00
commit d1dfdf4738
23 changed files with 352 additions and 294 deletions

View File

@ -54,7 +54,7 @@ export default function addChat(meetingId, message) {
}; };
const cb = (err, numChanged) => { const cb = (err, numChanged) => {
if (err != null) { if (err) {
Logger.error(`Adding chat to collection: ${err}`); Logger.error(`Adding chat to collection: ${err}`);
} }

View File

@ -1,15 +1,3 @@
import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearUsersCollection';
import clearChats from '/imports/api/chat/server/modifiers/clearChats';
import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection';
import { clearSlidesCollection } from '/imports/api/slides/server/modifiers/clearSlidesCollection';
import { clearPresentationsCollection }
from '/imports/api/presentations/server/modifiers/clearPresentationsCollection';
import { clearMeetingsCollection }
from '/imports/api/meetings/server/modifiers/clearMeetingsCollection';
import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection';
import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection';
import { clearCaptionsCollection }
from '/imports/api/captions/server/modifiers/clearCaptionsCollection';
import { logger } from '/imports/startup/server/logger'; import { logger } from '/imports/startup/server/logger';
import { redisPubSub } from '/imports/startup/server'; import { redisPubSub } from '/imports/startup/server';
import { BREAK_LINE, CARRIAGE_RETURN, NEW_LINE } from '/imports/utils/lineEndings.js'; import { BREAK_LINE, CARRIAGE_RETURN, NEW_LINE } from '/imports/utils/lineEndings.js';
@ -24,42 +12,6 @@ export function appendMessageHeader(eventName, messageObj) {
return messageObj; return messageObj;
}; };
export function clearCollections() {
console.log('in function clearCollections');
/*
This is to prevent collection clearing in development environment when the server
refreshes. Related to: https://github.com/meteor/meteor/issues/6576
*/
if (process.env.NODE_ENV === 'development') {
return;
}
const meetingId = arguments[0];
if (meetingId != null) {
clearUsersCollection(meetingId);
clearChats(meetingId);
clearMeetingsCollection(meetingId);
clearShapesCollection(meetingId);
clearSlidesCollection(meetingId);
clearPresentationsCollection(meetingId);
clearPollCollection(meetingId);
clearCursorCollection(meetingId);
clearCaptionsCollection(meetingId);
} else {
clearUsersCollection();
clearChats();
clearMeetingsCollection();
clearShapesCollection();
clearSlidesCollection();
clearPresentationsCollection();
clearPollCollection();
clearCursorCollection();
clearCaptionsCollection();
}
}
export const indexOf = [].indexOf || function (item) { export const indexOf = [].indexOf || function (item) {
for (let i = 0, l = this.length; i < l; i++) { for (let i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item) { if (i in this && this[i] === item) {

View File

@ -0,0 +1,17 @@
import RedisPubSub from '/imports/startup/server/redis';
import handleMeetingDestruction from './handlers/meetingDestruction';
import handleRecordingStatusChange from './handlers/recordingStatusChange';
import handlePermissionSettingsChange from './handlers/permissionSettingsChange';
import handleMeetingCreation from './handlers/meetingCreation';
import handleGetAllMettings from './handlers/getAllMeetings';
import handleStunTurnReply from './handlers/stunTurnReply';
RedisPubSub.on('meeting_destroyed_event', handleMeetingDestruction);
RedisPubSub.on('meeting_ended_message', handleMeetingDestruction);
RedisPubSub.on('end_and_kick_all_message', handleMeetingDestruction);
RedisPubSub.on('disconnect_all_users_message', handleMeetingDestruction);
RedisPubSub.on('recording_status_changed_message', handleRecordingStatusChange);
RedisPubSub.on('new_permission_settings', handlePermissionSettingsChange);
RedisPubSub.on('meeting_created_message', handleMeetingCreation);
RedisPubSub.on('get_all_meetings_reply_message', handleGetAllMettings);
RedisPubSub.on('send_stun_turn_info_reply_message', handleStunTurnReply);

View File

@ -0,0 +1,36 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Meetings from '/imports/api/meetings';
import addMeeting from '../modifiers/addMeeting';
import removeMeeting from '../modifiers/removeMeeting';
export default function handleGetAllMettings({ payload }) {
let meetings = payload.meetings;
check(meetings, Array);
// We need to map the meetings payload because for some reason this payload
// is different than the `meeting_created_message` one
meetings = meetings.map(m => ({
meeting_id: m.meetingID,
name: m.meetingName,
recorded: m.recorded,
voice_conf: m.voiceBridge,
duration: m.duration,
}));
const meetingsIds = meetings.map(m => m.meeting_id);
const meetingsToRemove = Meetings.find({
meetingId: { $nin: meetingsIds },
}).fetch();
meetingsToRemove.forEach(meeting => removeMeeting(meeting.meetingId));
let meetingsAdded = [];
meetings.forEach(meeting => {
meetingsAdded.push(addMeeting(meeting));
});
return meetingsAdded;
};

View File

@ -0,0 +1,11 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import addMeeting from '../modifiers/addMeeting';
export default function handleMeetingCreation({ payload }) {
const meetingId = payload.meeting_id;
check(meetingId, String);
return addMeeting(payload);
};

View File

@ -0,0 +1,11 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import removeMeeting from '../modifiers/removeMeeting';
export default function handleMeetingDestruction({ payload }) {
const meetingId = payload.meeting_id;
check(meetingId, String);
return removeMeeting(meetingId);
};

View File

@ -0,0 +1,52 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Meetings from '/imports/api/meetings';
import handleLockingMic from '/imports/api/users/server/modifiers/handleLockingMic';
export default function handlePermissionSettingsChange({ payload }) {
const meetingId = payload.meeting_id;
const permissions = payload.permissions;
check(meetingId, String);
check(permissions, Object);
const selector = {
meetingId,
};
const Meeting = Meetings.findOne(selector);
if (!Meeting) {
throw new Meteor.error('meeting-not-found', `Meeting id=${meetingId} was not found`);
}
const modifier = {
$set: {
roomLockSettings: {
disablePrivateChat: permissions.disablePrivateChat,
disableCam: permissions.disableCam,
disableMic: permissions.disableMic,
lockOnJoin: permissions.lockOnJoin,
lockedLayout: permissions.lockedLayout,
disablePublicChat: permissions.disablePublicChat,
lockOnJoinConfigurable: permissions.lockOnJoinConfigurable,
},
},
};
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Updating meeting permissions: ${err}`);
}
if (permissions.disableMic) {
handleLockingMic(meetingId, permissions);
}
if (numChanged) {
return Logger.info(`Updated meeting permissions id=${meetingId}`);
}
};
return Meetings.update(selector, modifier, cb);
};

View File

@ -0,0 +1,36 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Meetings from '/imports/api/meetings';
export default function handleRecordingStatusChange({ payload }) {
const meetingId = arg.payload.meeting_id;
const intendedForRecording = arg.payload.recorded;
const currentlyBeingRecorded = arg.payload.recording;
check(meetingId, String);
check(intendedForRecording, Boolean);
check(currentlyBeingRecorded, Boolean);
const selector = {
meetingId,
intendedForRecording,
};
const modifier = {
$set: {
currentlyBeingRecorded,
},
};
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Updating meeting recording status: ${err}`);
}
if (numChanged) {
return Logger.info(`Updated meeting recording status id=${meetingId}`);
}
};
return Meetings.update(selector, modifier, cb);
};

View File

@ -0,0 +1,33 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Meetings from '/imports/api/meetings';
export default function handleStunTurnReply({ payload }) {
const meetingId = payload.meeting_id;
const { stuns, turns } = payload;
check(meetingId, String);
const selector = {
meetingId,
};
const modifier = {
$set: {
stuns,
turns,
},
};
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Updating meeting stuns/turns: ${err}`);
}
if (numChanged) {
return Logger.info(`Updated meeting stuns/turns id=${meetingId}`);
}
};
return Meetings.update(selector, modifier, cb);
};

View File

@ -0,0 +1,3 @@
import './eventHandlers';
import './methods';
import './publishers';

View File

@ -0,0 +1,4 @@
import { Meteor } from 'meteor/meteor';
Meteor.methods({
});

View File

@ -0,0 +1,55 @@
import { check } from 'meteor/check';
import Meetings from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger';
import { initializeCursor } from '/imports/api/cursor/server/modifiers/initializeCursor';
export default function addMeeting(meeting) {
const APP_CONFIG = Meteor.settings.public.app;
const meetingId = meeting.meeting_id;
check(meeting, Object);
check(meetingId, String);
const selector = {
meetingId,
};
const modifier = {
$set: {
meetingId,
meetingName: meeting.name,
intendedForRecording: meeting.recorded,
currentlyBeingRecorded: false,
voiceConf: meeting.voice_conf,
duration: meeting.duration,
roomLockSettings: {
disablePrivateChat: false,
disableCam: false,
disableMic: false,
lockOnJoin: APP_CONFIG.lockOnJoin,
lockedLayout: false,
disablePublicChat: false,
lockOnJoinConfigurable: false,
},
},
};
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Adding meeting to collection: ${err}`);
}
initializeCursor(meetingId);
const { insertedId } = numChanged;
if (insertedId) {
return Logger.info(`Added meeting id=${meetingId}`);
}
if (numChanged) {
return Logger.info(`Upserted meeting id=${meetingId}`);
}
};
return Meetings.upsert(selector, modifier, cb);
};

View File

@ -1,48 +0,0 @@
import { initializeCursor } from '/imports/api/cursor/server/modifiers/initializeCursor';
import Meetings from '/imports/api/meetings';
import { logger } from '/imports/startup/server/logger';
export function addMeetingToCollection(meetingId, name, intendedForRecording,
voiceConf, duration, callback) {
const APP_CONFIG = Meteor.settings.public.app;
//check if the meeting is already in the collection
Meetings.upsert({
meetingId: meetingId,
}, {
$set: {
meetingName: name,
intendedForRecording: intendedForRecording,
currentlyBeingRecorded: false,
voiceConf: voiceConf,
duration: duration,
roomLockSettings: {
// by default the lock settings will be disabled on meeting create
disablePrivateChat: false,
disableCam: false,
disableMic: false,
lockOnJoin: APP_CONFIG.lockOnJoin,
lockedLayout: false,
disablePublicChat: false,
lockOnJoinConfigurable: false, // TODO
},
},
}, (_this => function (err, numChanged) {
let funct;
if (numChanged.insertedId != null) {
funct = function (cbk) {
logger.info(`__added MEETING ${meetingId}`);
return cbk();
};
return funct(callback);
} else {
logger.info('the meeting already existed so no information was added');
return callback();
}
}
)(this));
// initialize the cursor in the meeting
return initializeCursor(meetingId);
};

View File

@ -0,0 +1,29 @@
import Meetings from '/imports/api/chat';
import Logger from '/imports/startup/server/logger';
import removeMeeting from './removeMeeting';
import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearUsersCollection';
import clearChats from '/imports/api/chat/server/modifiers/clearChats';
import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection';
import { clearSlidesCollection } from '/imports/api/slides/server/modifiers/clearSlidesCollection';
import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection';
import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection';
import { clearCaptionsCollection }
from '/imports/api/captions/server/modifiers/clearCaptionsCollection';
import { clearPresentationsCollection }
from '/imports/api/presentations/server/modifiers/clearPresentationsCollection';
export default function clearMeetings() {
return Meetings.remove({}, (err) => {
clearCaptionsCollection();
clearChats();
clearCursorCollection();
clearPollCollection();
clearPresentationsCollection();
clearShapesCollection();
clearSlidesCollection();
clearUsersCollection();
return Logger.info('Cleared Meetings (all)');
});
};

View File

@ -1,13 +0,0 @@
import Meetings from '/imports/api/meetings';
import { logger } from '/imports/startup/server/logger';
export function clearMeetingsCollection() {
const meetingId = arguments[0];
if (meetingId != null) {
return Meetings.remove({
meetingId: meetingId,
}, logger.info(`cleared Meetings Collection (meetingId: ${meetingId}!`));
} else {
return Meetings.remove({}, logger.info('cleared Meetings Collection (all meetings)!'));
}
};

View File

@ -1,121 +0,0 @@
import { eventEmitter } from '/imports/startup/server';
import { logger } from '/imports/startup/server/logger';
import Meetings from '/imports/api/meetings';
import { handleLockingMic } from '/imports/api/users/server/modifiers/handleLockingMic';
import { addMeetingToCollection } from './addMeetingToCollection';
import { removeMeetingFromCollection } from './removeMeetingFromCollection';
eventEmitter.on('meeting_ended_message', function (arg) {
handleEndOfMeeting(arg);
});
eventEmitter.on('send_stun_turn_info_reply_message', function (arg) {
const stuns = arg.payload.stuns;
const turns = arg.payload.turns;
const meetingId = arg.payload.meeting_id;
Meetings.update({
meetingId: meetingId,
}, {
$set: {
stuns: stuns,
turns: turns,
},
});
return arg.callback();
});
eventEmitter.on('meeting_destroyed_event', function (arg) {
handleEndOfMeeting(arg);
});
eventEmitter.on('recording_status_changed_message', function (arg) {
const intendedForRecording = arg.payload.recorded;
const currentlyBeingRecorded = arg.payload.recording;
const meetingId = arg.payload.meeting_id;
Meetings.update({
meetingId: meetingId,
intendedForRecording: intendedForRecording,
}, {
$set: {
currentlyBeingRecorded: currentlyBeingRecorded,
},
});
return arg.callback();
});
eventEmitter.on('new_permission_settings', function (arg) {
const meetingId = arg.payload.meeting_id;
const payload = arg.payload;
const meetingObject = Meetings.findOne({
meetingId: meetingId,
});
if (meetingObject != null && payload != null) {
const oldSettings = meetingObject.roomLockSettings;
const newSettings = payload.permissions;
// if the disableMic setting was turned on
if (oldSettings != null && !oldSettings.disableMic && newSettings.disableMic) {
handleLockingMic(meetingId, newSettings);
}
const settingsObj = {
disablePrivateChat: newSettings.disablePrivateChat,
disableCam: newSettings.disableCam,
disableMic: newSettings.disableMic,
lockOnJoin: newSettings.lockOnJoin,
lockedLayout: newSettings.lockedLayout,
disablePublicChat: newSettings.disablePublicChat,
lockOnJoinConfigurable: newSettings.lockOnJoinConfigurable,
};
// substitute with the new lock settings
Meetings.update({
meetingId: meetingId,
}, {
$set: {
roomLockSettings: settingsObj,
},
});
}
return arg.callback();
});
eventEmitter.on('meeting_created_message', function (arg) {
const meetingName = arg.payload.name;
const intendedForRecording = arg.payload.recorded;
const voiceConf = arg.payload.voice_conf;
const duration = arg.payload.duration;
const meetingId = arg.payload.meeting_id;
return addMeetingToCollection(meetingId, meetingName, intendedForRecording,
voiceConf, duration, arg.callback);
});
eventEmitter.on('get_all_meetings_reply_message', function (arg) {
logger.info('Let\'s store some data for the running meetings so that when an' +
' HTML5 client joins everything is ready!');
logger.info(JSON.stringify(arg.payload));
const listOfMeetings = arg.payload.meetings;
// Processing the meetings recursively with a callback to notify us,
// ensuring that we update the meeting collection serially
let processMeeting = function () {
let meeting = listOfMeetings.pop();
if (meeting != null) {
return addMeetingToCollection(meeting.meetingID, meeting.meetingName,
meeting.recorded, meeting.voiceBridge, meeting.duration, processMeeting);
} else {
return arg.callback(); // all meeting arrays (if any) have been processed
}
};
return processMeeting();
});
export const handleEndOfMeeting = function (arg) {
const meetingId = arg.payload.meeting_id;
logger.info(`DESTROYING MEETING ${meetingId}`);
return removeMeetingFromCollection(meetingId, arg.callback);
};

View File

@ -0,0 +1,43 @@
import { check } from 'meteor/check';
import Meetings from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger';
import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearUsersCollection';
import clearChats from '/imports/api/chat/server/modifiers/clearChats';
import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection';
import { clearSlidesCollection } from '/imports/api/slides/server/modifiers/clearSlidesCollection';
import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection';
import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection';
import { clearCaptionsCollection }
from '/imports/api/captions/server/modifiers/clearCaptionsCollection';
import { clearPresentationsCollection }
from '/imports/api/presentations/server/modifiers/clearPresentationsCollection';
export default function removeMeeting(meetingId) {
check(meetingId, String);
const selector = {
meetingId,
};
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Removing meeting from collection: ${err}`);
}
if (numChanged) {
clearCaptionsCollection(meetingId);
clearChats(meetingId);
clearCursorCollection(meetingId);
clearPollCollection(meetingId);
clearPresentationsCollection(meetingId);
clearShapesCollection(meetingId);
clearSlidesCollection(meetingId);
clearUsersCollection(meetingId);
return Logger.info(`Removed meeting id=${meetingId}`);
}
};
return Meetings.remove(selector, cb);
};

View File

@ -1,23 +0,0 @@
import { clearCollections } from '/imports/api/common/server/helpers';
import Meetings from '/imports/api/meetings';
import { logger } from '/imports/startup/server/logger';
//clean up upon a meeting's end
export function removeMeetingFromCollection(meetingId, callback) {
if (Meetings.findOne({
meetingId: meetingId,
}) != null) {
logger.info(`end of meeting ${meetingId}. Clear the meeting data from all collections`);
clearCollections(meetingId);
return callback();
} else {
let funct = function (localCallback) {
logger.error(`Error! There was no such meeting ${meetingId}`);
return localCallback();
};
return funct(callback);
}
};

View File

@ -1,10 +0,0 @@
import Meetings from '/imports/api/meetings';
import { logger } from '/imports/startup/server/logger';
Meteor.publish('meetings', function (credentials) {
const { meetingId } = credentials;
logger.info(`publishing meetings for ${meetingId}`);
return Meetings.find({
meetingId: meetingId,
});
});

View File

@ -0,0 +1,18 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Meetings from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger';
Meteor.publish('meetings', (credentials) => {
const { meetingId, requesterUserId, requesterToken } = credentials;
check(meetingId, String);
check(requesterUserId, String);
check(requesterToken, String);
Logger.info(`Publishing meeting=${meetingId} ${requesterUserId} ${requesterToken}`);
return Meetings.find({
meetingId,
});
});

View File

@ -1,6 +1,5 @@
import { logger } from '/imports/startup/server/logger'; import { logger } from '/imports/startup/server/logger';
import { eventEmitter } from '/imports/startup/server'; import { eventEmitter } from '/imports/startup/server';
import { handleEndOfMeeting } from '/imports/api/meetings/server/modifiers/eventHandlers';
import { userJoined } from './userJoined'; import { userJoined } from './userJoined';
import { setUserLockedStatus } from './setUserLockedStatus'; import { setUserLockedStatus } from './setUserLockedStatus';
import { markUserOffline } from './markUserOffline'; import { markUserOffline } from './markUserOffline';
@ -15,14 +14,6 @@ eventEmitter.on('disconnect_user_message', function (arg) {
handleRemoveUserEvent(arg); handleRemoveUserEvent(arg);
}); });
eventEmitter.on('end_and_kick_all_message', function (arg) {
handleEndOfMeeting(arg);
});
eventEmitter.on('disconnect_all_users_message', function (arg) {
handleEndOfMeeting(arg);
});
eventEmitter.on('user_left_message', function (arg) { eventEmitter.on('user_left_message', function (arg) {
handleRemoveUserEvent(arg); handleRemoveUserEvent(arg);
}); });

View File

@ -1,25 +1,11 @@
import { Meteor } from 'meteor/meteor';
import Locales from '/imports/locales'; import Locales from '/imports/locales';
import Logger from './logger'; import Logger from './logger';
import Redis from './redis'; import Redis from './redis';
import { clearCollections } from '/imports/api/common/server/helpers';
Meteor.startup(() => { Meteor.startup(() => {
clearCollections();
const APP_CONFIG = Meteor.settings.public.app; const APP_CONFIG = Meteor.settings.public.app;
Logger.info(`SERVER STARTED. ENV=${Meteor.settings.runtime.env}`, APP_CONFIG);
let determineConnectionType = function () {
let baseConnection = 'HTTP';
if (APP_CONFIG.httpsConnection) {
baseConnection += ('S');
}
return baseConnection;
};
Logger.info(`server start. Connection type:${determineConnectionType()}`);
Logger.info('APP_CONFIG=');
Logger.info(APP_CONFIG);
Logger.info('Running in environment type:' + Meteor.settings.runtime.env);
}); });
WebApp.connectHandlers.use('/check', (req, res, next) => { WebApp.connectHandlers.use('/check', (req, res, next) => {

View File

@ -13,11 +13,7 @@ import '/imports/api/deskshare/server/modifiers/handleDeskShareChange';
import '/imports/api/deskshare/server/modifiers/handleIncomingDeskshareMessage'; import '/imports/api/deskshare/server/modifiers/handleIncomingDeskshareMessage';
import '/imports/api/deskshare/server/modifiers/eventHandlers'; import '/imports/api/deskshare/server/modifiers/eventHandlers';
import '/imports/api/meetings/server/publications'; import '/imports/api/meetings/server';
import '/imports/api/meetings/server/modifiers/addMeetingToCollection';
import '/imports/api/meetings/server/modifiers/clearMeetingsCollection';
import '/imports/api/meetings/server/modifiers/removeMeetingFromCollection';
import '/imports/api/meetings/server/modifiers/eventHandlers';
import '/imports/api/phone/server/modifiers/eventHandlers'; import '/imports/api/phone/server/modifiers/eventHandlers';