Feature: new "report a bug" modal (#13530)

* WIP: report a bug api/ui

* fix lint

* add USER_USERNAME

* extend sendTxn tests / checks + fix bug report email

* fix lint

* add more checks to sendTxn - fix bug-report variables

* fix lint / ci

* fix test: reset email config url

* fix test stub

* fix tests

* refactor the variables checks

* lint.

* move bug-report page as a modal

* send user_email to the email

* show true/false instead 1/0

* fix issues

* fix footer report bug email if not logged in

* fix styles/margins

* prefill user's email

* show facebook email if local email not existing

* bugReportSuccessModal.vue

* add BROWSER_UA to mail properties

* extract bugReportLogic to its own lib file for unit test

* test api validators

* fix lint
This commit is contained in:
negue
2021-12-15 02:16:50 +01:00
committed by GitHub
parent c37dac5568
commit a1cddcaf17
25 changed files with 833 additions and 64 deletions
@@ -0,0 +1,53 @@
import { authWithHeaders } from '../../middlewares/auth';
import { bugReportLogic } from '../../libs/bug-report';
const api = {};
/**
* @api {post} /api/v4/bug-report Report an issue
* @apiName BugReport
* @apiGroup BugReport
* @apiDescription This POST method is used to send bug reports from the Website.
* Since it needs the Users Data, it requires authentication.
*
* @apiParam (Body) {String} message Bug Report Message to sent
* @apiParam (Body) {String} email User Email
*
* @apiSuccess {Object} data Result of this bug report
* @apiSuccess {Boolean} data.ok Status of this report
* @apiSuccess {String} data.message Status of this report
*
* @apiError (400) {BadRequest} emptyReportBugMessage The report message is missing.
* @apiUse UserNotFound
*/
api.bugReport = {
method: 'POST',
url: '/bug-report',
middlewares: [authWithHeaders()],
async handler (req, res) {
req.checkBody('message', res.t('emptyReportBugMessage')).notEmpty();
req.checkBody('email', res.t('missingEmail')).notEmpty();
req.checkBody('email', res.t('notAnEmail')).isEmail();
const validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
const { message, email } = req.body;
const { user } = res.locals;
const BROWSER_UA = req.get('User-Agent');
const {
emailData, sendMailResult,
} = bugReportLogic(
user, email, message, BROWSER_UA,
);
res.status(200).send({
ok: true,
emailData,
sendMailResult,
});
},
};
export default api;
+39
View File
@@ -0,0 +1,39 @@
import nconf from 'nconf';
import { convertVariableObjectToArray, sendTxn } from './email';
export async function bugReportLogic (
user, userEmail, message, BROWSER_UA,
) {
const emailData = {
USER_ID: user._id,
USER_EMAIL: userEmail,
USER_USERNAME: user.auth.local.username,
USER_LEVEL: user.stats.lvl,
USER_CLASS: user.stats.class,
USER_DAILIES_PAUSED: user.preferences.sleep === 1 ? 'true' : 'false',
USER_COSTUME: user.preferences.costume === 1 ? 'true' : 'false',
USER_CUSTOM_DAY: user.preferences.dayStart,
USER_TIMEZONE_OFFSET: user.preferences.timezoneOffset,
USER_SUBSCRIPTION: user.purchased.plan.planId,
USER_PAYMENT_PLATFORM: user.purchased.plan.paymentMethod,
USER_CUSTOMER_ID: user.purchased.plan.customerId,
USER_CONSECUTIVE_MONTHS: user.purchased.plan.consecutive.count,
USER_OFFSET_MONTHS: user.purchased.plan.consecutive.offset,
USER_HOURGLASSES: user.purchased.plan.consecutive.trinkets,
REPORT_MSG: message,
BROWSER_UA,
};
const adminMail = { email: nconf.get('ADMIN_EMAIL') };
const sendMailResult = await sendTxn(
adminMail,
'report-a-bug',
convertVariableObjectToArray(emailData),
);
return {
sendMailResult,
emailData,
};
}
+35 -1
View File
@@ -67,12 +67,32 @@ export function getGroupUrl (group) {
// Send a transactional email using Mandrill through the external email server
export async function sendTxn (mailingInfoArray, emailType, variables, personalVariables) {
mailingInfoArray = Array.isArray(mailingInfoArray) ? mailingInfoArray : [mailingInfoArray]; // eslint-disable-line no-param-reassign, max-len
if (!Array.isArray(mailingInfoArray)) {
mailingInfoArray = [mailingInfoArray]; // eslint-disable-line no-param-reassign
}
for (const entry of mailingInfoArray) {
if (typeof entry === 'string'
&& (typeof entry._id === 'undefined' && typeof entry.email === 'undefined')
) {
throw new Error('Argument Error mailingInfoArray: does not contain email or _id');
}
}
if (variables && !Array.isArray(variables)) {
throw new Error('Argument Error variables: is not an array');
}
variables = [ // eslint-disable-line no-param-reassign
{ name: 'BASE_URL', content: BASE_URL },
].concat(variables || []);
for (const variable of variables) {
if (typeof variable.name === 'undefined' && typeof variable.content === 'undefined') {
throw new Error('Argument Error variables: does not contain name or content');
}
}
// It's important to pass at least a user with its `preferences`
// as we need to check if he unsubscribed
mailingInfoArray = mailingInfoArray // eslint-disable-line no-param-reassign
@@ -157,3 +177,17 @@ export async function sendTxn (mailingInfoArray, emailType, variables, personalV
return null;
}
export function convertVariableObjectToArray (variableObject) {
const variablesArray = [];
const objectKeys = Object.keys(variableObject);
for (const propName of objectKeys) {
variablesArray.push({
name: propName,
content: variableObject[propName],
});
}
return variablesArray;
}