Add check for member being in two groups when match is created
Some checks are pending
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run

This commit is contained in:
Kalle 2025-04-10 21:53:21 +03:00
parent b635a788a0
commit a9febdd989
2 changed files with 52 additions and 6 deletions

View File

@ -8,8 +8,14 @@ import {
matchMapList,
} from "~/features/sendouq-match/core/match.server";
import * as QRepository from "~/features/sendouq/QRepository.server";
import type { LookingGroupWithInviteCode } from "~/features/sendouq/q-types";
import invariant from "~/utils/invariant";
import { errorToastIfFalsy, parseRequestPayload } from "~/utils/remix.server";
import { logger } from "~/utils/logger";
import {
errorToast,
errorToastIfFalsy,
parseRequestPayload,
} from "~/utils/remix.server";
import { errorIsSqliteForeignKeyConstraintFailure } from "~/utils/sql";
import { assertUnreachable } from "~/utils/types";
import { SENDOUQ_PAGE, sendouQMatchPage } from "~/utils/urls";
@ -232,6 +238,21 @@ export const action: ActionFunction = async ({ request }) => {
ignoreModePreferences: data._action === "MATCH_UP_RECHALLENGE",
},
);
const memberInManyGroups = verifyNoMemberInTwoGroups(
[...ourGroup.members, ...theirGroup.members],
lookingGroups,
);
if (memberInManyGroups) {
logger.error("User in two groups preventing match creation", {
userId: memberInManyGroups.id,
});
errorToast(
`${memberInManyGroups.username} is in two groups so match can't be started`,
);
}
const createdMatch = createMatch({
alphaGroupId: ourGroup.id,
bravoGroupId: theirGroup.id,
@ -367,3 +388,23 @@ export const action: ActionFunction = async ({ request }) => {
return null;
};
/** Sanity check that no member is in two groups due to a bug or race condition.
*
* @returns null if no member is in two groups, otherwise return the problematic member
*/
function verifyNoMemberInTwoGroups(
members: LookingGroupWithInviteCode["members"],
allGroups: LookingGroupWithInviteCode[],
) {
for (const member of members) {
if (
allGroups.filter((group) => group.members.some((m) => m.id === member.id))
.length > 1
) {
return member;
}
}
return null;
}

View File

@ -55,7 +55,7 @@ export function parseSearchParams<T extends z.ZodTypeAny>({
} catch (e) {
logger.error("Error parsing search params", e);
throw errorToast("Validation failed");
throw errorToastRedirect("Validation failed");
}
}
@ -93,7 +93,7 @@ export async function parseRequestPayload<T extends z.ZodTypeAny>({
} catch (e) {
logger.error("Error parsing request payload", e);
throw errorToast("Validation failed");
throw errorToastRedirect("Validation failed");
}
}
@ -117,7 +117,7 @@ export async function parseFormData<T extends z.ZodTypeAny>({
} catch (e) {
logger.error("Error parsing form data", e);
throw errorToast("Validation failed");
throw errorToastRedirect("Validation failed");
}
}
@ -183,7 +183,7 @@ function formDataToObject(formData: FormData) {
// TODO: investigate better solution to toasts when middlewares land (current one has a problem of clearing search params)
export function errorToast(message: string) {
export function errorToastRedirect(message: string) {
return redirect(`?__error=${message}`);
}
@ -194,7 +194,12 @@ export function errorToastIfFalsy(
): asserts condition {
if (condition) return;
throw errorToast(message);
throw errorToastRedirect(message);
}
/** Throws a redirect triggering an error toast with given message. */
export function errorToast(message: string) {
throw errorToastRedirect(message);
}
export function successToast(message: string) {