Skip to content

Commit

Permalink
Merge pull request #300 from dataforgoodfr/fix/D4G-293-transform-emai…
Browse files Browse the repository at this point in the history
…l-to-lowercase

fix: Prevent uppercased letters in email
  • Loading branch information
Baboo7 authored Jul 7, 2023
2 parents cd4c6a9 + 97c1fc5 commit 8236135
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 14 deletions.
21 changes: 16 additions & 5 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,22 @@
"no-restricted-imports": [
"error",
{
"name": "axios",
"importNames": [
"default"
],
"message": "Please use `http`, the axios client configured in the request utils (src/utils/request)."
"paths": [
{
"name": "axios",
"importNames": [
"default"
],
"message": "Please use `http`, the axios client configured in the request utils (src/utils/request)."
},
{
"name": "react-hook-form",
"importNames": [
"useForm"
],
"message": "Use our custom `useForm` hook instead which relies on react-hook-form (src/modules/common/hooks)."
}
]
}
],
"@typescript-eslint/no-use-before-define": "off"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, TextField, Grid, Button, Typography } from "@mui/material";
import { useForm, Controller } from "react-hook-form";
import { Controller } from "react-hook-form";
import { useMutation, useQuery, useQueryClient } from "react-query";
import SaveIcon from "@mui/icons-material/Save";
import { SuccessAlert, ErrorAlert } from "../../../alert";
Expand All @@ -8,6 +8,7 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
import { IGame } from "../../../../utils/types";
import { http } from "../../../../utils/request";
import { useForm } from "../../../common/hooks/useForm";

export { GameInfo };

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Box, Button, Container, Paper, Typography } from "@mui/material";
import SaveIcon from "@mui/icons-material/Save";
import { AxiosError } from "axios";
import { useForm } from "react-hook-form";
import { useMutation, useQueryClient } from "react-query";

import { ErrorAlert, SuccessAlert } from "../../alert";
Expand All @@ -10,6 +9,7 @@ import { User } from "../../users/types";
import { useAuth } from "../../auth/authProvider";
import { getCountryByCode } from "../../signup/components/SelectCountry";
import { http } from "../../../utils/request";
import { useForm } from "../../common/hooks/useForm";

export { Settings };

Expand Down
31 changes: 31 additions & 0 deletions packages/client/src/modules/common/hooks/useForm.formatters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { FieldValues } from "react-hook-form";

export { formatData };

const FIELD_NAME_TO_FORMATTER: Record<
string,
{
formatter: (value: string) => string;
}
> = {
email: {
formatter: formatEmail,
},
};

function formatEmail(email: string = ""): string {
return email.trim().toLowerCase();
}

function formatData<TFieldValues extends FieldValues = FieldValues>(
data: TFieldValues
): TFieldValues {
return Object.fromEntries(
Object.entries(data).map(([fieldName, fieldValue]) => {
const formatter =
FIELD_NAME_TO_FORMATTER[fieldName]?.formatter ||
((value: any) => value);
return [fieldName, formatter(fieldValue)];
})
) as TFieldValues;
}
45 changes: 45 additions & 0 deletions packages/client/src/modules/common/hooks/useForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useCallback, useMemo } from "react";
import {
// eslint-disable-next-line no-restricted-imports
useForm as useFormLib,
FieldValues,
UseFormProps,
UseFormReturn,
UseFormHandleSubmit,
SubmitHandler,
SubmitErrorHandler,
} from "react-hook-form";
import { formatData } from "./useForm.formatters";

export { useForm };

const useForm = <
TFieldValues extends FieldValues = FieldValues,
TContext = any
>(
props?: UseFormProps<TFieldValues, TContext>
): UseFormReturn<TFieldValues, TContext> => {
const form = useFormLib(props);

const handleSubmit: UseFormHandleSubmit<TFieldValues> = useCallback(
(
onValid: SubmitHandler<TFieldValues>,
onInvalid?: SubmitErrorHandler<TFieldValues>
) => {
return form.handleSubmit((data: TFieldValues, ...args) => {
onValid(formatData(data), ...args);
}, onInvalid);
},
[form]
);

const newForm = useMemo(
() => ({
...form,
handleSubmit,
}),
[form, handleSubmit]
);

return newForm;
};
2 changes: 1 addition & 1 deletion packages/client/src/modules/magic-link/MagicLink.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useForm } from "react-hook-form";
import { useForm } from "../common/hooks/useForm";
import FormInput from "../common/components/FormInput";
import { Link } from "react-router-dom";
import { sendMagicLink } from "../users/services";
Expand Down
3 changes: 2 additions & 1 deletion packages/client/src/modules/play/MyGames/MyGames.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { trim } from "lodash";
import { Box, CircularProgress, Grid, TextField } from "@mui/material";
import { Controller, useForm } from "react-hook-form";
import { Controller } from "react-hook-form";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { ErrorAlert } from "../../alert";
import { PlayBox } from "../Components";
Expand All @@ -17,6 +17,7 @@ import { useTranslation } from "../../translations/useTranslation";
import { Typography } from "../../common/components/Typography";
import { useAlerts } from "../../alert/AlertProvider";
import { Button } from "../../common/components/Button";
import { useForm } from "../../common/hooks/useForm";

export { MyGames };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Button, Grid, Tooltip, Typography } from "@mui/material";
import { CustomContainer } from "./styles/personalization";
import { BackArrow, BackArrowWithValidation } from "./common/BackArrow";
import { QuestionLine, QuestionText } from "./styles/form";
import { useForm } from "react-hook-form";
import { useForm } from "../../common/hooks/useForm";
import { PersoFormInputList, PersoFormNumberInput } from "./common/FormInputs";
import {
formSections,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FormInput from "../../../common/components/FormInput";
import { useForm } from "react-hook-form";
import { useForm } from "../../../common/hooks/useForm";
import CheckboxWithText from "../CheckboxWithText";
import { NewUser } from "../../../users/services";
import { useMutation } from "react-query";
Expand Down
10 changes: 7 additions & 3 deletions packages/server/src/modules/users/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { rolesServices } from "../../roles/services";
import { services } from "../services";
import { signInController } from "./signInController";
import { getManyUsersController } from "./getManyUsersController";
import { getEmailSchema } from "../../utils/schemaParser";

const crudController = {
getDocumentController,
Expand All @@ -27,7 +28,7 @@ export { controllers };
async function signUpController(request: Request, response: Response) {
const bodySchema = z.object({
country: z.string(),
email: z.string(),
email: getEmailSchema(),
lastName: z.string(),
firstName: z.string(),
});
Expand Down Expand Up @@ -57,7 +58,10 @@ async function getLoggedUserController(request: Request, response: Response) {
}

async function sendMagicLinkController(request: Request, response: Response) {
const { email } = request.body;
const bodySchema = z.object({
email: getEmailSchema(),
});
const { email } = bodySchema.parse(request.body);
await services.sendMagicLink(email);
response
.status(200)
Expand All @@ -76,7 +80,7 @@ async function getTeamForPlayer(request: Request, response: Response) {

const bodySchemaUpdate = z.object({
country: z.string().optional(),
email: z.string().optional(),
email: getEmailSchema(),
lastName: z.string().optional(),
firstName: z.string().optional(),
roleId: z.number().optional(),
Expand Down
9 changes: 9 additions & 0 deletions packages/server/src/modules/utils/schemaParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { z } from "zod";

export { getEmailSchema };

const getEmailSchema = () =>
z
.string()
.email()
.transform((email) => (email || "").trim().toLowerCase());

0 comments on commit 8236135

Please sign in to comment.