Merge pull request #19328 from gustavotrott/guest-wait-graphql

refactor: Refactor guest-wait page to fetch data from Hasura
This commit is contained in:
Gustavo Trott 2023-12-13 17:38:11 -03:00 committed by GitHub
commit 780b496ba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 146 additions and 80 deletions

View File

@ -19,6 +19,7 @@ case class MeetingDbModel(
presentationUploadExternalDescription: String,
presentationUploadExternalUrl: String,
learningDashboardAccessToken: String,
logoutUrl: String,
createdTime: Long,
durationInSeconds: Int
)
@ -36,6 +37,7 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
presentationUploadExternalDescription,
presentationUploadExternalUrl,
learningDashboardAccessToken,
logoutUrl,
createdTime,
durationInSeconds
) <> (MeetingDbModel.tupled, MeetingDbModel.unapply)
@ -50,6 +52,7 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
val presentationUploadExternalDescription = column[String]("presentationUploadExternalDescription")
val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl")
val learningDashboardAccessToken = column[String]("learningDashboardAccessToken")
val logoutUrl = column[String]("logoutUrl")
val createdTime = column[Long]("createdTime")
val durationInSeconds = column[Int]("durationInSeconds")
}
@ -70,6 +73,7 @@ object MeetingDAO {
presentationUploadExternalDescription = meetingProps.meetingProp.presentationUploadExternalDescription,
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
logoutUrl = meetingProps.systemProps.logoutUrl,
createdTime = meetingProps.durationProps.createdTime,
durationInSeconds = meetingProps.durationProps.duration * 60
)

View File

@ -68,7 +68,8 @@ case class LockSettingsProps(
)
case class SystemProps(
html5InstanceId: Int
html5InstanceId: Int,
logoutUrl: String,
)
case class GroupProps(

View File

@ -411,7 +411,7 @@ public class MeetingService implements MessageListener {
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(),
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(),
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), m.getLogoutUrl(),
m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(),
m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl(),
m.getOverrideClientSettings());

View File

@ -42,6 +42,7 @@ public interface IBbbWebApiGWApp {
BreakoutRoomsParams breakoutParams,
LockSettingsParams lockSettingsParams,
Integer html5InstanceId,
String logoutUrl,
ArrayList<Group> groups,
ArrayList<String> disabledFeatures,
Boolean notifyRecordingIsOn,

View File

@ -149,6 +149,7 @@ class BbbWebApiGWApp(
breakoutParams: BreakoutRoomsParams,
lockSettingsParams: LockSettingsParams,
html5InstanceId: java.lang.Integer,
logoutUrl: String,
groups: java.util.ArrayList[Group],
disabledFeatures: java.util.ArrayList[String],
notifyRecordingIsOn: java.lang.Boolean,
@ -230,7 +231,8 @@ class BbbWebApiGWApp(
)
val systemProps = SystemProps(
html5InstanceId
html5InstanceId,
logoutUrl
)
val groupsAsVector: Vector[GroupProps] = groups.asScala.toVector.map(g => GroupProps(g.getGroupId(), g.getName(), g.getUsersExtId().asScala.toVector))

View File

@ -14,3 +14,11 @@ location /v1/graphql {
#proxy_pass http://127.0.0.1:8080; #Hasura
proxy_pass http://127.0.0.1:8378; #Graphql Middleware
}
location /api/rest {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080; #Hasura
}

View File

@ -20,6 +20,7 @@ create table "meeting" (
"presentationUploadExternalUrl" varchar(500),
"learningDashboardAccessToken" varchar(100),
"html5InstanceId" varchar(100),
"logoutUrl" varchar(500),
"createdTime" bigint,
"durationInSeconds" integer
);
@ -398,7 +399,7 @@ AS SELECT "user"."userId",
"user"."raiseHand",
"user"."emoji",
"user"."guest",
-- "user"."guestStatus",
"user"."guestStatus",
"user"."mobile",
"user"."clientType",
"user"."enforceLayout",
@ -437,10 +438,10 @@ rank() OVER (
) as "positionInWaitingQueue",
u."isAllowed",
u."isDenied",
COALESCE(u."guestLobbyMessage",mup."guestLobbyMessage") AS "guestLobbyMessage"
COALESCE(NULLIF(u."guestLobbyMessage",''),NULLIF(mup."guestLobbyMessage",'')) AS "guestLobbyMessage"
FROM "user" u
JOIN "meeting_usersPolicies" mup using("meetingId")
where u."guestStatus" != 'ALLOW';
where u."guestStatus" = 'WAIT';
--v_user_ref will be used only as foreign key (not possible to fetch this table directly through graphql)
--it is necessary because v_user has some conditions like "lockSettings-hideUserList"

View File

@ -13,3 +13,11 @@ location /v1/graphql {
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080; #Hasura
}
location /api/rest {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080; #Hasura
}

View File

@ -1 +1,3 @@
[]
- collection: allowed-queries
scope:
global: true

View File

@ -154,3 +154,13 @@ select_permissions:
filter:
meetingId:
_eq: X-Hasura-MeetingId
- role: pre_join_bbb_client
permission:
columns:
- logoutUrl
- meetingId
- name
filter:
meetingId:
_eq: X-Hasura-MeetingId
comment: ""

View File

@ -25,7 +25,7 @@ object_relationships:
remote_table:
name: v_user_customParameter
schema: public
- name: guestStatus
- name: guestStatusDetails
using:
manual_configuration:
column_mapping:
@ -146,6 +146,7 @@ select_permissions:
- expired
- extId
- guest
- guestStatus
- hasDrawPermissionOnCurrentPage
- isDialIn
- isModerator
@ -182,6 +183,7 @@ select_permissions:
- expired
- extId
- guest
- guestStatus
- joinErrorCode
- joinErrorMessage
- joined

View File

@ -34,3 +34,13 @@ select_permissions:
- meetingId:
_eq: X-Hasura-ModeratorInMeeting
allow_aggregations: true
- role: pre_join_bbb_client
permission:
columns:
- guestLobbyMessage
- guestStatus
- positionInWaitingQueue
filter:
userId:
_eq: X-Hasura-UserId
comment: ""

View File

@ -1 +1,20 @@
[]
- name: allowed-queries
definition:
queries:
- name: UserCurrent
query: |
query UserCurrent {
user_current {
userId
name
guestStatus
guestStatusDetails {
guestLobbyMessage
positionInWaitingQueue
}
meeting {
name
logoutUrl
}
}
}

View File

@ -1 +1,9 @@
[]
- comment: ""
definition:
query:
collection_name: allowed-queries
query_name: UserCurrent
methods:
- GET
name: UserCurrent
url: usercurrent

View File

@ -131,6 +131,7 @@ export default async function addMeeting(meeting) {
},
systemProps: {
html5InstanceId: Number,
logoutUrl: String,
},
groups: Array,
overrideClientSettings: String,

View File

@ -22,9 +22,7 @@ subscription userCurrentSubscription {
emoji
extId
guest
guestStatus {
guestStatus
}
guestStatus
hasDrawPermissionOnCurrentPage
isDialIn
isModerator

View File

@ -152,7 +152,7 @@
let positionInWaitingQueue = '';
function updatePositionInWaitingQueue(newPositionInWaitingQueue) {
if (positionInWaitingQueue !== newPositionInWaitingQueue) {
positionInWaitingQueue = newPositionInWaitingQueue;
positionInWaitingQueue = newPositionInWaitingQueue.toString();
if (positionInWaitingQueue === '1') {
updatePositionOnPage(_('app.guest.firstPositionInWaitingQueue'), '');
} else {
@ -173,6 +173,15 @@
return null;
}
function getClientJoinUrl() {
const joinEndpoint = '/html5client/join';
const sessionToken = getSearchParam('sessionToken');
const url = new URL(`${window.location.origin}${joinEndpoint}`);
url.search = `sessionToken=${sessionToken}`;
return url;
}
async function fetchLocalizedMessages() {
const DEFAULT_LANGUAGE = 'en';
const LOCALES_ENDPOINT = '/html5client/locale';
@ -264,43 +273,16 @@
const sessionToken = getSearchParam('sessionToken');
if (!sessionToken) {
disableAnimation()
disableAnimation();
updateMessage(_('app.guest.noSessionToken'));
return;
}
//First, check that we already have a response
const statusFromStorage = sessionStorage.getItem(`guestStatus_${sessionToken}`);
if(statusFromStorage) {
stopUpdatingWaitingPosition();
const statusParsed = JSON.parse(statusFromStorage);
const { status, response } = statusParsed;
if(status === 'ALLOW'){
updateLobbyMessage(_('app.guest.allow'));
setTimeout(() => {
disableAnimation();
window.location = response.url;
}, MESSAGE_TIMEOUT);
} else {
redirect(
_('app.guest.' + response.messageKey),
response.url,
);
}
return;
}
pollGuestStatus(sessionToken, 0);
} catch (e) {
disableAnimation();
console.error(e);
updateMessage(_('app.guest.errorSeeConsole'));
}
})
.catch((e) => {
console.error(e);
@ -308,15 +290,21 @@
});
}
function fetchGuestWait(sessionToken) {
const GUEST_WAIT_ENDPOINT = '/bigbluebutton/api/guestWait';
function fetchUserCurrent(sessionToken) {
const GUEST_WAIT_ENDPOINT = '/api/rest/usercurrent/';
const url = new URL(`${window.location.origin}${GUEST_WAIT_ENDPOINT}`);
url.search = `sessionToken=${sessionToken}&redirect=false`;
return fetch(url, { method: 'get' });
const headers = new Headers({
'X-Session-Token': sessionToken,
'Content-Type': 'application/json',
});
return fetch(url, { method: 'get', headers: headers });
};
function redirect(message, url) {
disableAnimation();
stopUpdatingWaitingPosition();
updateMessage(message);
setTimeout(() => {
window.location = url;
@ -326,47 +314,50 @@
function pollGuestStatus(token, everyMs) {
setTimeout(function () {
fetchGuestWait(token)
.then(async (resp) => await resp.json())
.then((data) => {
const code = data.response.returncode;
fetchUserCurrent(token)
.then(async (resp) => await resp.json())
.then((data) => {
if(data.hasOwnProperty('error') || !data.hasOwnProperty('user_current')) {
disableAnimation();
stopUpdatingWaitingPosition();
updateMessage(_('app.guest.guestInvalid'));
return;
}
const userData = data.user_current[0];
const response = data.response;
if (userData.guestStatus === 'ALLOW') {
updateLobbyMessage(_('app.guest.allow'));
stopUpdatingWaitingPosition();
const saveStatusResponse = (status, response, token) => {
stopUpdatingWaitingPosition();
sessionStorage.setItem(`guestStatus_${token}`, JSON.stringify({ status, response }));
};
// Timeout is required by accessibility to allow viewing of the message for a minimum of 3 seconds
// before redirecting.
setTimeout(() => {
disableAnimation();
window.location = getClientJoinUrl();
}, MESSAGE_TIMEOUT);
return;
}
if (code === 'FAILED') {
saveStatusResponse(code, response, token);
return redirect(_('app.guest.' + data.response.messageKey), data.response.url);
}
if (userData.guestStatus === 'DENY') {
stopUpdatingWaitingPosition();
return redirect(_('app.guest.guestDeny'), userData.meeting.logoutUrl);
}
const status = data.response.guestStatus;
if (userData.guestStatus === 'WAIT') {
//Wait message will be set by `updateLobbyMessage`
//updateMessage(updateMessage(_('app.guest.guestWait')));
}
if (status === 'DENY') {
saveStatusResponse(status, response, token);
return redirect(_('app.guest.' + data.response.messageKey), data.response.url);
}
if(userData.guestStatusDetails !== null) {
updatePositionInWaitingQueue(userData.guestStatusDetails.positionInWaitingQueue);
updateLobbyMessage(userData.guestStatusDetails.guestLobbyMessage || '');
}
if (status === 'ALLOW') {
updateLobbyMessage(_('app.guest.allow'));
saveStatusResponse(status, response, token);
// Timeout is required by accessibility to allow viewing of the message for a minimum of 3 seconds
// before redirecting.
setTimeout(() => {
disableAnimation();
window.location = data.response.url;
}, MESSAGE_TIMEOUT);
return;
}
updatePositionInWaitingQueue(data.response.positionInWaitingQueue);
updateLobbyMessage(data.response.lobbyMessage);
const ATTEMPT_EVERY_MS = 10 * 1000; // 10 seconds
return pollGuestStatus(token, ATTEMPT_EVERY_MS);
});
const ATTEMPT_EVERY_MS = 10 * 1000; // 10 seconds
return pollGuestStatus(token, ATTEMPT_EVERY_MS);
});
}, everyMs);
};