From aea09a8da379955ca002f83bcadc19ce4f65af17 Mon Sep 17 00:00:00 2001 From: hmuresan Date: Mon, 24 May 2021 13:05:37 +0300 Subject: [PATCH] Send feedback metadata to JaaS feedback endpoint --- conference.js | 30 +++++--------- react/features/base/jwt/middleware.js | 4 +- react/features/feedback/actions.js | 60 +++++++++++++++++++++------ react/features/feedback/functions.js | 50 ++++++++++++++++++++++ react/features/feedback/logger.js | 5 +++ 5 files changed, 115 insertions(+), 34 deletions(-) create mode 100644 react/features/feedback/functions.js create mode 100644 react/features/feedback/logger.js diff --git a/conference.js b/conference.js index 83c12683c..619ae7e03 100644 --- a/conference.js +++ b/conference.js @@ -1085,17 +1085,6 @@ export default { return room && room.isCallstatsEnabled(); }, - /** - * Sends the given feedback through CallStats if enabled. - * - * @param overallFeedback an integer between 1 and 5 indicating the - * user feedback - * @param detailedFeedback detailed feedback from the user. Not yet used - */ - sendFeedback(overallFeedback, detailedFeedback) { - return room.sendFeedback(overallFeedback, detailedFeedback); - }, - /** * Get speaker stats that track total dominant speaker time. * @@ -2803,14 +2792,15 @@ export default { requestFeedbackPromise = Promise.resolve(true); } - // All promises are returning Promise.resolve to make Promise.all to - // be resolved when both Promises are finished. Otherwise Promise.all - // will reject on first rejected Promise and we can redirect the page - // before all operations are done. - Promise.all([ - requestFeedbackPromise, - this.leaveRoomAndDisconnect() - ]).then(values => { + let feedbackResult; + + requestFeedbackPromise + .then(res => { + feedbackResult = res; + + return this.leaveRoomAndDisconnect(); + }) + .then(() => { this._room = undefined; room = undefined; @@ -2822,7 +2812,7 @@ export default { if (!interfaceConfig.SHOW_PROMOTIONAL_CLOSE_PAGE) { APP.API.notifyReadyToClose(); } - APP.store.dispatch(maybeRedirectToWelcomePage(values[0])); + APP.store.dispatch(maybeRedirectToWelcomePage(feedbackResult)); }); }, diff --git a/react/features/base/jwt/middleware.js b/react/features/base/jwt/middleware.js index 5060d3841..a81047f3f 100644 --- a/react/features/base/jwt/middleware.js +++ b/react/features/base/jwt/middleware.js @@ -134,7 +134,7 @@ function _setJWT(store, next, action) { } if (jwtPayload) { - const { context, iss } = jwtPayload; + const { context, iss, sub } = jwtPayload; action.jwt = jwt; action.issuer = iss; @@ -144,7 +144,7 @@ function _setJWT(store, next, action) { action.callee = context.callee; action.group = context.group; action.server = context.server; - action.tenant = context.tenant; + action.tenant = context.tenant || sub || undefined; action.user = user; user && _overwriteLocalParticipant( diff --git a/react/features/feedback/actions.js b/react/features/feedback/actions.js index d72f4eeea..5760d05e7 100644 --- a/react/features/feedback/actions.js +++ b/react/features/feedback/actions.js @@ -4,6 +4,8 @@ import type { Dispatch } from 'redux'; import { FEEDBACK_REQUEST_IN_PROGRESS } from '../../../modules/UI/UIErrors'; import { openDialog } from '../base/dialog'; +import { isVpaasMeeting } from '../billing-counter/functions'; +import { extractFqnFromPath } from '../dynamic-branding/functions'; import { CANCEL_FEEDBACK, @@ -11,9 +13,9 @@ import { SUBMIT_FEEDBACK_SUCCESS } from './actionTypes'; import { FeedbackDialog } from './components'; +import { sendFeedbackToJaaSRequest } from './functions'; declare var config: Object; -declare var interfaceConfig: Object; /** * Caches the passed in feedback in the redux store. @@ -110,6 +112,39 @@ export function openFeedbackDialog(conference: Object, onClose: ?Function) { }); } +/** + * Sends feedback metadata to JaaS endpoint. + * + * @param {JitsiConference} conference - The JitsiConference that is being rated. + * @param {Object} feedback - The feedback message and score. + * + * @returns {Promise} + */ +export function sendJaasFeedbackMetadata(conference: Object, feedback: Object) { + return (dispatch: Dispatch, getState: Function): Promise => { + const state = getState(); + const { jaasFeedbackMetadataURL } = state['features/base/config']; + + const { jwt, user, tenant } = state['features/base/jwt']; + + if (!isVpaasMeeting(state) || !jaasFeedbackMetadataURL) { + return Promise.resolve(); + } + + const meetingFqn = extractFqnFromPath(state['features/base/connection'].locationURL.pathname); + const feedbackData = { + ...feedback, + sessionId: conference.getMeetingUniqueId(), + userId: user.id, + meetingFqn, + jwt, + tenant + }; + + return sendFeedbackToJaaSRequest(jaasFeedbackMetadataURL, feedbackData); + }; +} + /** * Send the passed in feedback. * @@ -125,16 +160,17 @@ export function submitFeedback( score: number, message: string, conference: Object) { - return (dispatch: Dispatch) => conference.sendFeedback(score, message) - .then( - () => dispatch({ type: SUBMIT_FEEDBACK_SUCCESS }), - error => { - dispatch({ - type: SUBMIT_FEEDBACK_ERROR, - error - }); + return (dispatch: Dispatch) => + conference.sendFeedback(score, message) + .then(() => dispatch({ type: SUBMIT_FEEDBACK_SUCCESS })) + .then(() => dispatch(sendJaasFeedbackMetadata(conference, { score, + message })) + .catch(error => { + dispatch({ + type: SUBMIT_FEEDBACK_ERROR, + error + }); - return Promise.reject(error); - } - ); + return Promise.reject(error); + })); } diff --git a/react/features/feedback/functions.js b/react/features/feedback/functions.js new file mode 100644 index 000000000..bdc14ae50 --- /dev/null +++ b/react/features/feedback/functions.js @@ -0,0 +1,50 @@ +// @flow + +import logger from './logger'; + +/** + * Sends feedback metadata to JaaS endpoints. + * + * @param {string} url - The JaaS metadata endpoint URL. + * @param {Object} feedbackData - The feedback data object. + * @returns {Promise} + */ +export async function sendFeedbackToJaaSRequest(url: string, feedbackData: Object) { + const { + jwt, + sessionId, + meetingFqn, + score, + message, + userId, + tenant + } = feedbackData; + const headers = { + 'Authorization': `Bearer ${jwt}`, + 'Content-Type': 'application/json' + }; + const data = { + sessionId, + meetingFqn, + userId, + tenant, + submitted: new Date().getTime(), + rating: score, + comments: message + }; + + try { + const res = await fetch(url, { + method: 'POST', + headers, + body: JSON.stringify(data) + }); + + if (!res.ok) { + logger.error('Status error:', res.status); + } + } catch (err) { + logger.error('Could not send request', err); + } +} + diff --git a/react/features/feedback/logger.js b/react/features/feedback/logger.js new file mode 100644 index 000000000..9ab94be31 --- /dev/null +++ b/react/features/feedback/logger.js @@ -0,0 +1,5 @@ +// @flow + +import { getLogger } from '../base/logging/functions'; + +export default getLogger('features/feedback');