From addfcd3658958988747d50272856b8c35a02a120 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Thu, 11 Sep 2025 01:22:02 +0200 Subject: [PATCH] refactor user/checkName to return the status --- backend/__tests__/api/controllers/user.spec.ts | 15 +++++++++------ backend/src/api/controllers/user.ts | 10 +++++----- frontend/src/ts/modals/google-sign-up.ts | 13 +++++++------ frontend/src/ts/modals/simple-modals.ts | 13 +++++++------ frontend/src/ts/pages/login.ts | 13 +++++++------ packages/contracts/src/index.ts | 2 +- packages/contracts/src/users.ts | 10 ++++++++-- 7 files changed, 44 insertions(+), 32 deletions(-) diff --git a/backend/__tests__/api/controllers/user.spec.ts b/backend/__tests__/api/controllers/user.spec.ts index ccf447ce6..796fc73d6 100644 --- a/backend/__tests__/api/controllers/user.spec.ts +++ b/backend/__tests__/api/controllers/user.spec.ts @@ -238,8 +238,8 @@ describe("user controller test", () => { //THEN expect(body).toEqual({ - message: "Username available", - data: null, + message: "Check username", + data: { availability: "available" }, }); expect(userIsNameAvailableMock).toHaveBeenCalledWith("bob", ""); }); @@ -252,10 +252,13 @@ describe("user controller test", () => { const { body } = await mockApp .get("/users/checkName/bob") //no authentication required - .expect(409); + .expect(200); //THEN - expect(body.message).toEqual("Username unavailable"); + expect(body).toEqual({ + message: "Check username", + data: { availability: "taken" }, + }); expect(userIsNameAvailableMock).toHaveBeenCalledWith("bob", ""); }); @@ -271,8 +274,8 @@ describe("user controller test", () => { //THEN expect(body).toEqual({ - message: "Username available", - data: null, + message: "Check username", + data: { availability: "available" }, }); expect(userIsNameAvailableMock).toHaveBeenCalledWith("bob", uid); }); diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index b93110516..7cc2892f7 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -51,6 +51,7 @@ import { AddTagRequest, AddTagResponse, CheckNamePathParameters, + CheckNameResponse, CreateUserRequest, DeleteCustomThemeRequest, EditCustomThemeRequst, @@ -430,16 +431,15 @@ export async function optOutOfLeaderboards( export async function checkName( req: MonkeyRequest -): Promise { +): Promise { const { name } = req.params; const { uid } = req.ctx.decodedToken; const available = await UserDAL.isNameAvailable(name, uid); - if (!available) { - throw new MonkeyError(409, "Username unavailable"); - } - return new MonkeyResponse("Username available", null); + return new MonkeyResponse("Check username", { + availability: available ? "available" : "taken", + }); } export async function updateEmail( diff --git a/frontend/src/ts/modals/google-sign-up.ts b/frontend/src/ts/modals/google-sign-up.ts index ff9d9481c..6727a8b14 100644 --- a/frontend/src/ts/modals/google-sign-up.ts +++ b/frontend/src/ts/modals/google-sign-up.ts @@ -155,13 +155,14 @@ function disableInput(): void { validateWithIndicator(nameInputEl, { schema: UserNameSchema, isValid: async (name: string) => { - const checkNameResponse = ( - await Ape.users.getNameAvailability({ - params: { name: name }, - }) - ).status; + const checkNameResponse = await Ape.users.getNameAvailability({ + params: { name: name }, + }); - return checkNameResponse === 200 ? true : "Name not available"; + return checkNameResponse.status === 200 && + checkNameResponse.body.data.availability === "available" + ? true + : "Name not available"; }, debounceDelay: 1000, callback: (result) => { diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index ee02b42fe..9cce4512f 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -476,13 +476,14 @@ list.updateName = new SimpleModal({ validation: { schema: UserNameSchema, isValid: async (newName: string) => { - const checkNameResponse = ( - await Ape.users.getNameAvailability({ - params: { name: newName }, - }) - ).status; + const checkNameResponse = await Ape.users.getNameAvailability({ + params: { name: newName }, + }); - return checkNameResponse === 200 ? true : "Name not available"; + return checkNameResponse.status === 200 && + checkNameResponse.body.data.availability === "available" + ? true + : "Name not available"; }, debounceDelay: 1000, }, diff --git a/frontend/src/ts/pages/login.ts b/frontend/src/ts/pages/login.ts index 5d1d0b84f..0f7e1a53f 100644 --- a/frontend/src/ts/pages/login.ts +++ b/frontend/src/ts/pages/login.ts @@ -64,13 +64,14 @@ const nameInputEl = document.querySelector( validateWithIndicator(nameInputEl, { schema: UserNameSchema, isValid: async (name: string) => { - const checkNameResponse = ( - await Ape.users.getNameAvailability({ - params: { name: name }, - }) - ).status; + const checkNameResponse = await Ape.users.getNameAvailability({ + params: { name: name }, + }); - return checkNameResponse === 200 ? true : "Name not available"; + return checkNameResponse.status === 200 && + checkNameResponse.body.data.availability === "available" + ? true + : "Name not available"; }, debounceDelay: 1000, callback: (result) => { diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 6a54be9f5..4f9920fe7 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -37,5 +37,5 @@ export const contract = c.router({ * Whenever there is a breaking change with old frontend clients increase this number. * This will inform the frontend to refresh. */ -export const COMPATIBILITY_CHECK = 3; +export const COMPATIBILITY_CHECK = 4; export const COMPATIBILITY_CHECK_HEADER = "X-Compatibility-Check"; diff --git a/packages/contracts/src/users.ts b/packages/contracts/src/users.ts index 548d3000d..b861078e1 100644 --- a/packages/contracts/src/users.ts +++ b/packages/contracts/src/users.ts @@ -59,6 +59,13 @@ export type CheckNamePathParameters = z.infer< typeof CheckNamePathParametersSchema >; +export const CheckNameResponseSchema = responseWithData( + z.object({ + availability: z.enum(["available", "taken"]), + }) +); +export type CheckNameResponse = z.infer; + export const UpdateUserNameRequestSchema = z.object({ name: UserNameSchema, }); @@ -364,8 +371,7 @@ export const usersContract = c.router( path: "/checkName/:name", pathParams: CheckNamePathParametersSchema.strict(), responses: { - 200: MonkeyResponseSchema.describe("Name is available"), - 409: MonkeyResponseSchema.describe("Name is not available"), + 200: CheckNameResponseSchema, }, metadata: meta({ authenticationOptions: { isPublic: true },