Skip to content

fix: manage backend authXService errors #3190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 26, 2022

Conversation

AbhineetJain
Copy link
Contributor

@AbhineetJain AbhineetJain commented Jul 25, 2022

This PR shows backend errors from authXService on relevant pages/forms. The following errors were considered in this PR:

  • Login
  • Get methods
  • Update password
  • Update username

Subtasks

  • used the ErrorSummary component to display the general error message
  • implemented util to extract field errors from API response and integrate with Formik
  • updated the implementations for the errors listed above
  • fixed unit tests
  • fixed stories
  • added stories

There are still some unused errors that are stored in the authXService context. This PR makes some progress towards #3088

Screenshots

Screen Shot 2022-07-25 at 4 54 12 PM

Screen Shot 2022-07-25 at 4 54 44 PM

Screen Shot 2022-07-25 at 4 55 56 PM

Screen Shot 2022-07-25 at 4 56 36 PM

@AbhineetJain AbhineetJain requested a review from a team as a code owner July 25, 2022 20:51
@@ -45,6 +46,17 @@ export const getFormHelpers =
}
}

export const getFormHelpersWithError = <T>(
form: FormikContextType<T>,
error?: Error | unknown,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised error is optional in this fn.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, yeah. This will eventually replace getFormHelpers, but we want to be able to replace it piecemeal so we had to give it a different name. It's called withError because the functionality it adds is unpacking an error (so that we don't have to repeat that code everywhere). A different name would be fine though!

@@ -107,42 +97,44 @@ export const SignInForm: FC<SignInFormProps> = ({
validateOnBlur: false,
onSubmit,
})
const getFieldHelpers = getFormHelpers<BuiltInAuthFormValues>(form)
const getFieldHelpers = getFormHelpersWithError<BuiltInAuthFormValues>(form, authError)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm being dense :) but why do we call this method with authError and not methodsError?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

methodsError is not linked to the sign-in form. This method attributes form errors to specific fields in the form.

)}
{methodsError && (
<ErrorSummary error={methodsError} defaultMessage={Language.methodsErrorMessage} />
)}
Copy link
Member

@Kira-Pilot Kira-Pilot Jul 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small concern I have with this pattern is that JSX complexity will grow 1:1 with API/Error complexity (if I am understanding this right). Let's say we had a more complex form that made a couple of different API calls (like 4). Would we be passing 4 additional props and rendering 4 of the above fragments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we were debating whether handling multiple errors in the same component made sense, or if we could just render a separate ErrorSummary component for each error. Since we were not sure how common it was to have many parallel API calls with a single form, we thought this would be fine for the time being. The use case might get clearer as we explore more of the code base.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right @Kira-Pilot. My reason for not wanting to coalesce the errors into one ErrorSummary was aesthetic (I didn't think two error summaries stuck together would look good given that sometimes they have collapsible parts). But you raise a good point. It might be a good idea to make our next ticket one that can render a ton of errors on one page in order to find out up front if it's a problem and what the solution would be.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. Leaving for now seems reasonable to me! We can cross the complexity bridge when we get there.

authErrorMessage={authErrorMessage}
methodsErrorMessage={getMethodsError}
authError={authState.context.authError}
methodsError={authState.context.getMethodsError as Error}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to override the compiler here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getMethodsError does not really throw an error from the backend, but only a generic one. This was being done earlier (in one of the lines we deleted), so just wanted to keep it the same for sanity (like referencing the message string from the Error object).

Additionally, I don't know a good way to produce a getMethodsError, so couldn't reeally play around with it. @kylecarbs do you have any suggestions about how the error could be reproduced?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really think we do have to override it. I'm guessing that ended up in there originally to make it so we didn't have to add | unknown in a component prop type. I had hoped we could narrow all the error types in the XServices but it turns out we want to leave them alone until they reach ErrorSummary and getFormHelpers, so I think we're just stuck with Error | unknown and we can delete this. I like the comment explaining that this call never returns an ApiError though.

@Kira-Pilot
Copy link
Member

Couple of questions but this is looking sweet! I think it will bring a lot of clarity to our forms.

@AbhineetJain
Copy link
Contributor Author

AbhineetJain commented Jul 26, 2022

The UI snapshots with errors are not showing the specific form field errors, because the field is not "touched". @presleyp @Kira-Pilot Any ideas on how we can simulate that? Example

@presleyp
Copy link
Contributor

The UI snapshots with errors are not showing the specific form field errors, because the field is not "touched". @presleyp @Kira-Pilot Any ideas on how we can simulate that? Example

Oh yeah, I've run into that before! I don't think I fixed it though haha. But let's see if there's a programmatic way to make it be touched. It looks like there is a way: https://formik.org/docs/api/formik#initialtouched-formiktouchedvalues. I don't know how easy that will be to work with given our custom util function - let me know if you want to pair on it.

@Kira-Pilot
Copy link
Member

The UI snapshots with errors are not showing the specific form field errors, because the field is not "touched". @presleyp @Kira-Pilot Any ideas on how we can simulate that? Example

@AbhineetJain The best solution I can think of (and maybe there is a better one) is to add a new boolean prop to your component called shouldDirtyForm or something. When shouldDirtyForm is truthy, you can call Formik's setTouched method, which will imperatively dirty the field. Then, in your story, you can pass this prop through as true.

If you decide to use this strategy, you should make sure to add a comment so future devs know that your new prop is for testing only. :)

@AbhineetJain
Copy link
Contributor Author

@presleyp @Kira-Pilot Thank you so much for these amazing suggestions. I used initialTouched via a new prop and marked it for testing only.

@AbhineetJain AbhineetJain merged commit 0128ca6 into main Jul 26, 2022
@AbhineetJain AbhineetJain deleted the abhineetjain/auth-x-service-errors branch July 26, 2022 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants