Skip to content

Commit

Permalink
Unique Resident IDs (#200)
Browse files Browse the repository at this point in the history
* Added uq constraint

* Finalized impl of toasts

* Resolved issue w updating resident, fixed toast display order when creating resident

* Clean up

* Fixed

* Cleaned print statements

* Nuked comments

* Addressed feedback on error types and error helpers ; modal no longer closes on duplicate resident

* No longer closes on error for editing residents
  • Loading branch information
kevin-pierce authored Dec 3, 2023
1 parent 59f41e0 commit 129fba0
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 23 deletions.
21 changes: 21 additions & 0 deletions backend/app/rest/residents_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ def add_resident():
400,
)

# Check for the existence of a resident prior to adding them
fmt_resident_id = resident.get("initial") + str(resident.get("room_num"))
try:
res = residents_service.get_residents(False, 1, 10, fmt_resident_id)
if len(res["residents"]) > 0:
return jsonify({"error": "Resident already with id {fmt_resident_id} already exists".format(fmt_resident_id=fmt_resident_id)}), 409
except Exception as e:
error_message = getattr(e, "message", None)
return jsonify({"error": (error_message if error_message else str(e))}), 500

try:
created_resident = residents_service.add_resident(resident)
return jsonify(created_resident), 201
Expand All @@ -41,6 +51,17 @@ def update_resident(resident_id):
jsonify({"date_left_error": "date_left cannot be less than date_joined"}),
400,
)

# Check for the existence of a resident prior to adding them
fmt_resident_id = updated_resident.get("initial") + str(updated_resident.get("room_num"))
try:
res = residents_service.get_residents(False, 1, 10, fmt_resident_id)
if len(res["residents"]) == 1 and res["residents"][0]["id"] != resident_id:
return jsonify({"error": "Resident with id {fmt_resident_id} already exists".format(fmt_resident_id=fmt_resident_id)}), 409
except Exception as e:
error_message = getattr(e, "message", None)
return jsonify({"error": (error_message if error_message else str(e))}), 500


try:
updated_resident = residents_service.update_resident(
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/APIClients/AuthAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { AxiosError } from "axios";
import {

Check warning on line 7 in frontend/src/APIClients/AuthAPIClient.ts

View workflow job for this annotation

GitHub Actions / run-lint

Replace `⏎··getAuthErrMessage⏎}·⏎` with `getAuthErrMessage·}·`
getAuthErrMessage
}
from "../helper/authError";
from "../helper/error";
import AUTHENTICATED_USER_KEY from "../constants/AuthConstants";
import {

Check warning on line 12 in frontend/src/APIClients/AuthAPIClient.ts

View workflow job for this annotation

GitHub Actions / run-lint

Replace `⏎··AuthenticatedUser,⏎··AuthTokenResponse,⏎` with `·AuthenticatedUser,·AuthTokenResponse·`
AuthenticatedUser,
AuthTokenResponse,
ErrorResponse,
} from "../types/AuthTypes";
import { AuthErrorResponse } from "../types/ErrorTypes"

Check warning on line 16 in frontend/src/APIClients/AuthAPIClient.ts

View workflow job for this annotation

GitHub Actions / run-lint

Insert `;`
import baseAPIClient from "./BaseAPIClient";
import {
getLocalStorageObjProperty,
Expand All @@ -23,7 +23,7 @@ import {
const login = async (
email: string,
password: string,
): Promise<AuthTokenResponse | ErrorResponse> => {
): Promise<AuthTokenResponse | AuthErrorResponse> => {
try {
const { data } = await baseAPIClient.post(
"/auth/login",
Expand Down Expand Up @@ -102,7 +102,7 @@ const register = async (
lastName: string,
email: string,
password: string,
): Promise<AuthTokenResponse | ErrorResponse> => {
): Promise<AuthTokenResponse | AuthErrorResponse> => {
try {
const { data } = await baseAPIClient.post(
"/auth/register",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/APIClients/CommonAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AxiosError } from "axios";
import AUTHENTICATED_USER_KEY from "../constants/AuthConstants";
import { getLocalStorageObjProperty } from "../utils/LocalStorageUtils";
import baseAPIClient from "./BaseAPIClient";
import { ErrorResponse } from "../types/AuthTypes";
import { AuthErrorResponse } from "../types/ErrorTypes";

const inviteUser = async (
email: string,
Expand All @@ -26,7 +26,7 @@ const inviteUser = async (
}
};

const getUserStatus = async (email: string): Promise<string | ErrorResponse> => {
const getUserStatus = async (email: string): Promise<string | AuthErrorResponse> => {
try {
if (email === "") {
return "";
Expand Down
19 changes: 17 additions & 2 deletions frontend/src/APIClients/ResidentAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CreateResidentParams,
EditResidentParams,
} from "../types/ResidentTypes";
import { ResidentErrorResponse } from "../types/ErrorTypes"
import { getLocalStorageObjProperty } from "../utils/LocalStorageUtils";
import baseAPIClient from "./BaseAPIClient";

Expand Down Expand Up @@ -60,7 +61,7 @@ const createResident = async ({
roomNum,
dateJoined,
buildingId,
}: CreateResidentParams): Promise<boolean> => {
}: CreateResidentParams): Promise<boolean | ResidentErrorResponse> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
Expand All @@ -73,6 +74,13 @@ const createResident = async ({
);
return true;
} catch (error) {
const axiosErr = (error as any) as AxiosError;

if (axiosErr.response && axiosErr.response.status === 409) {
return {
errMessage: "Resident with the specified user ID already exists."
};
}
return false;
}
};
Expand Down Expand Up @@ -103,7 +111,7 @@ const editResident = async ({
dateJoined,
buildingId,
dateLeft,
}: EditResidentParams): Promise<boolean> => {
}: EditResidentParams): Promise<boolean | ResidentErrorResponse> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
Expand All @@ -116,6 +124,13 @@ const editResident = async ({
);
return true;
} catch (error) {
const axiosErr = (error as any) as AxiosError;

if (axiosErr.response && axiosErr.response.status === 409) {
return {
errMessage: "Resident with the specified user ID already exists."
};
}
return false;
}
};
Expand Down
26 changes: 20 additions & 6 deletions frontend/src/components/forms/CreateResident.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import {
import { AddIcon } from "@chakra-ui/icons";
import { SingleDatepicker } from "chakra-dayzed-datepicker";
import { Col, Row } from "react-bootstrap";
import CreateToast from "../common/Toasts";

import selectStyle from "../../theme/forms/selectStyles";
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
import ResidentAPIClient from "../../APIClients/ResidentAPIClient";
import BuildingAPIClient from "../../APIClients/BuildingAPIClient";
import { BuildingLabel } from "../../types/BuildingTypes";
import { convertToString } from "../../helper/dateHelpers";
import { isResidentErrorResponse } from "../../helper/error"

type Props = {
getRecords: (pageNumber: number) => Promise<void>;
Expand All @@ -57,17 +59,31 @@ const CreateResident = ({
const [isOpen, setIsOpen] = useState(false);
const [showAlert, setShowAlert] = useState(false);

const newToast = CreateToast();

const ROOM_ERROR_TEXT = `Room Number is required and must only contain numbers.`;
const addResident = async () => {
await ResidentAPIClient.createResident({
const res = await ResidentAPIClient.createResident({
initial: initials.toUpperCase(),
roomNum: parseInt(roomNumber, 10),
dateJoined: convertToString(moveInDate),
buildingId,
});
getRecords(1);
countResidents();
setUserPageNum(1);

if (isResidentErrorResponse(res)) {
newToast(
"Error creating resident",
res.errMessage,
"error"
)
}
else if (res) {
getRecords(1);
countResidents();
setUserPageNum(1);
setShowAlert(true)
setIsOpen(false);
}
};

const handleInitialsChange = (e: { target: { value: unknown } }) => {
Expand Down Expand Up @@ -154,8 +170,6 @@ const CreateResident = ({
}

addResident();
setIsOpen(false);
setShowAlert(true);
};

// Timer to remove alert
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/components/forms/EditResident.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import selectStyle from "../../theme/forms/selectStyles";
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
import CreateToast from "../common/Toasts";
import { convertToDate, convertToString } from "../../helper/dateHelpers";
import { isResidentErrorResponse } from "../../helper/error"

type Props = {
buildingOptions: BuildingLabel[],
Expand Down Expand Up @@ -76,13 +77,21 @@ const EditResident = ({
dateLeft: moveOutDate ? convertToString(moveOutDate) : undefined,
});

if (res != null) {
if (isResidentErrorResponse(res)) {
newToast(
"Error updating resident",
res.errMessage,
"error"
)
}
else if (res !== null && res) {
newToast(
"Resident updated",
"Resident has been successfully updated",
"success",
);
getRecords(userPageNum);
toggleClose();
} else {
newToast(
"Error updating resident",
Expand Down Expand Up @@ -176,7 +185,6 @@ const EditResident = ({
setRoomNumberError(false);
setMoveOutDateError(false);
setBuildingError(false);
toggleClose();
};

useEffect(() => {
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/components/forms/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import authAPIClient from "../../APIClients/AuthAPIClient";
import AUTHENTICATED_USER_KEY from "../../constants/AuthConstants";
import { HOME_PAGE, SIGNUP_PAGE } from "../../constants/Routes";
import AuthContext from "../../contexts/AuthContext";
import { ErrorResponse, AuthTokenResponse } from "../../types/AuthTypes";
import { AuthTokenResponse } from "../../types/AuthTypes";
import { AuthErrorResponse } from "../../types/ErrorTypes"
import commonApiClient from "../../APIClients/CommonAPIClient";
import { isAuthErrorResponse } from "../../helper/authError";
import { isAuthErrorResponse } from "../../helper/error";

type CredentialsProps = {
email: string;
Expand Down Expand Up @@ -75,7 +76,7 @@ const Login = ({
if (isInvited !== "Not Invited") {
const loginResponse:
| AuthTokenResponse
| ErrorResponse = await authAPIClient.login(email, password);
| AuthErrorResponse = await authAPIClient.login(email, password);
if (isAuthErrorResponse(loginResponse)) {
setPasswordError(true);
setPasswordErrStr(loginResponse.errMessage);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/forms/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { HOME_PAGE, LOGIN_PAGE } from "../../constants/Routes";
import AuthContext from "../../contexts/AuthContext";
import commonApiClient from "../../APIClients/CommonAPIClient";
import AUTHENTICATED_USER_KEY from "../../constants/AuthConstants";
import { isAuthErrorResponse } from "../../helper/authError";
import { isAuthErrorResponse } from "../../helper/error";

type SignupProps = {
email: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AxiosError } from "axios";
import { AuthTokenResponse, ErrorResponse, AuthFlow } from "../types/AuthTypes";
import { AuthTokenResponse, AuthFlow } from "../types/AuthTypes";
import { AuthErrorResponse, ResidentErrorResponse } from '../types/ErrorTypes'

export const getAuthErrMessage = (axiosErrRes: AxiosError["response"], flow: AuthFlow): string => {
if (axiosErrRes && axiosErrRes.data && axiosErrRes.data.error) {
Expand All @@ -9,7 +10,11 @@ export const getAuthErrMessage = (axiosErrRes: AxiosError["response"], flow: Aut
}

export const isAuthErrorResponse = (
res: string | AuthTokenResponse | ErrorResponse,
): res is ErrorResponse => {
res: string | AuthTokenResponse | AuthErrorResponse,
): res is AuthErrorResponse => {
return res !== null && typeof res !== 'string' && "errCode" in res;
};

export const isResidentErrorResponse = (res: boolean | ResidentErrorResponse) : res is ResidentErrorResponse => {
return (typeof res !== 'boolean' && 'errMessage' in res);
}
8 changes: 8 additions & 0 deletions frontend/src/types/ErrorTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type ResidentErrorResponse = {
errMessage: string;
}

export type AuthErrorResponse = {
errCode: number;
errMessage: string;
};

0 comments on commit 129fba0

Please sign in to comment.