From 69720cfe8cac554d91a8ac577175a3769b3c4cd6 Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 1 Jun 2022 19:13:36 +0000 Subject: [PATCH 1/2] Show error message from backend on create existing user --- site/src/api/errors.ts | 8 +++++++ .../CreateUserPage/CreateUserPage.test.tsx | 2 +- .../CreateUserPage/CreateUserPage.tsx | 4 ++-- site/src/xServices/users/usersXService.ts | 24 ++++++++++++------- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/site/src/api/errors.ts b/site/src/api/errors.ts index f16f422154848..052dd303f9e97 100644 --- a/site/src/api/errors.ts +++ b/site/src/api/errors.ts @@ -33,6 +33,14 @@ export const isApiError = (err: any): err is ApiError => { return false } +/** + * ApiErrors contain useful error messages in their response body. They contain an overall message + * and may also contain errors for specific form fields. + * @param error ApiError + * @returns true if the ApiError contains error messages for specific form fields. + */ +export const hasApiFieldErrors = (error: ApiError): boolean => Array.isArray(error.response.data.errors) + export const mapApiErrorToFieldErrors = (apiErrorResponse: ApiErrorResponse): FieldErrors => { const result: FieldErrors = {} diff --git a/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx b/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx index 0aae2f9f35695..714d10f54dd35 100644 --- a/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx +++ b/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx @@ -46,7 +46,7 @@ describe("Create User Page", () => { }) render() await fillForm({}) - const errorMessage = await screen.findByText(Language.unknownError) + const errorMessage = await screen.findByText(UserLanguage.createUserError) expect(errorMessage).toBeDefined() }) diff --git a/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.tsx b/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.tsx index 6b1bd0367ff7c..c5ddc98adcb3e 100644 --- a/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.tsx +++ b/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.tsx @@ -15,11 +15,11 @@ export const CreateUserPage: React.FC = () => { const xServices = useContext(XServiceContext) const myOrgId = useSelector(xServices.authXService, selectOrgId) const [usersState, usersSend] = useActor(xServices.usersXService) - const { createUserError, createUserFormErrors } = usersState.context + const { createUserErrorMessage, createUserFormErrors } = usersState.context const navigate = useNavigate() // There is no field for organization id in Community Edition, so handle its field error like a generic error const genericError = - createUserError || createUserFormErrors?.organization_id || !myOrgId ? Language.unknownError : undefined + createUserErrorMessage || createUserFormErrors?.organization_id || (!myOrgId ? Language.unknownError : undefined) return ( diff --git a/site/src/xServices/users/usersXService.ts b/site/src/xServices/users/usersXService.ts index 7566f17ed4623..abbd1d5cf49b6 100644 --- a/site/src/xServices/users/usersXService.ts +++ b/site/src/xServices/users/usersXService.ts @@ -1,25 +1,33 @@ import { assign, createMachine } from "xstate" import * as API from "../../api/api" -import { ApiError, FieldErrors, getErrorMessage, isApiError, mapApiErrorToFieldErrors } from "../../api/errors" +import { + ApiError, + FieldErrors, + getErrorMessage, + hasApiFieldErrors, + isApiError, + mapApiErrorToFieldErrors, +} from "../../api/errors" import * as TypesGen from "../../api/typesGenerated" import { displayError, displaySuccess } from "../../components/GlobalSnackbar/utils" import { generateRandomString } from "../../util/random" export const Language = { createUserSuccess: "Successfully created user.", + createUserError: "Error on creating the user.", suspendUserSuccess: "Successfully suspended the user.", - suspendUserError: "Error on suspend the user.", + suspendUserError: "Error on suspending the user.", resetUserPasswordSuccess: "Successfully updated the user password.", - resetUserPasswordError: "Error on reset the user password.", + resetUserPasswordError: "Error on resetting the user password.", updateUserRolesSuccess: "Successfully updated the user roles.", - updateUserRolesError: "Error on update the user roles.", + updateUserRolesError: "Error on updating the user roles.", } export interface UsersContext { // Get users users?: TypesGen.User[] getUsersError?: Error | unknown - createUserError?: Error | unknown + createUserErrorMessage?: string createUserFormErrors?: FieldErrors // Suspend user userIdToSuspend?: TypesGen.User["id"] @@ -122,7 +130,7 @@ export const usersMachine = createMachine( onError: [ { target: "idle", - cond: "isFormError", + cond: "hasFieldErrors", actions: ["assignCreateUserFormErrors"], }, { @@ -235,7 +243,7 @@ export const usersMachine = createMachine( }, }, guards: { - isFormError: (_, event) => isApiError(event.data), + hasFieldErrors: (_, event) => isApiError(event.data) && hasApiFieldErrors(event.data), }, actions: { assignUsers: assign({ @@ -258,7 +266,7 @@ export const usersMachine = createMachine( getUsersError: undefined, })), assignCreateUserError: assign({ - createUserError: (_, event) => event.data, + createUserErrorMessage: (_, event) => getErrorMessage(event.data, Language.createUserError), }), assignCreateUserFormErrors: assign({ // the guard ensures it is ApiError From 52f79a4a016c6e238eed44657df99850babb1dba Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 1 Jun 2022 19:19:29 +0000 Subject: [PATCH 2/2] Format --- site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx b/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx index 714d10f54dd35..907807afe47c4 100644 --- a/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx +++ b/site/src/pages/UsersPage/CreateUserPage/CreateUserPage.test.tsx @@ -7,7 +7,7 @@ import { Language as FooterLanguage } from "../../../components/FormFooter/FormF import { history, render } from "../../../testHelpers/renderHelpers" import { server } from "../../../testHelpers/server" import { Language as UserLanguage } from "../../../xServices/users/usersXService" -import { CreateUserPage, Language } from "./CreateUserPage" +import { CreateUserPage } from "./CreateUserPage" const fillForm = async ({ username = "someuser",