-
Notifications
You must be signed in to change notification settings - Fork 9
SANDBOX-1893 | Show alerts for user signup errors #102
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
Changes from all commits
31f2f20
98411f0
1ccf15f
15ad2dd
561b22b
6460078
c1c9fef
6468371
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| import { createMockResponse } from '../../test-utils/mockResponse'; | ||
| import UserSignupError from '../errors/UserSignupError'; | ||
|
|
||
| describe('user signup error', () => { | ||
| const defaultErrorMessage: string = | ||
| 'Unable to sign you up into Developer Sandbox. Please contact devsandbox@redhat.com'; | ||
|
|
||
| it('sets a message when using the main constructor', () => { | ||
| const errorMsg = 'test error msg'; | ||
|
|
||
| // Call the function under test. | ||
| const userSignupError = new UserSignupError(errorMsg); | ||
|
|
||
| expect(userSignupError.message).toBe(errorMsg); | ||
| }); | ||
|
|
||
| it('returns the default error message on internal server errors', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 500, | ||
| }); | ||
|
|
||
| // Call the function under test. | ||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe(defaultErrorMessage); | ||
| }); | ||
|
|
||
| it('returns the default error message when the response body is not valid JSON', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 400, | ||
| json: () => Promise.reject(new Error('invalid json')), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe(defaultErrorMessage); | ||
| }); | ||
|
|
||
| it('returns the default error message when the response body is null', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 400, | ||
| json: () => Promise.resolve(null), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe(defaultErrorMessage); | ||
| }); | ||
|
|
||
| it('returns the default error message when the response body has no message field', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 400, | ||
| json: () => Promise.resolve({ details: 'some detail' }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe(defaultErrorMessage); | ||
| }); | ||
|
|
||
| it('returns the invalid code message with details', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 403, | ||
| json: () => | ||
| Promise.resolve({ | ||
| message: 'invalid code provided', | ||
| details: 'code ABC123 is not recognized', | ||
| }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe( | ||
| 'The provided activation code is invalid: code ABC123 is not recognized', | ||
| ); | ||
| }); | ||
|
|
||
| it('returns the suspended message', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 403, | ||
| json: () => | ||
| Promise.resolve({ | ||
| message: 'user has been suspended', | ||
| }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe( | ||
| 'Access to the Developer Sandbox has been suspended due to suspicious activity or detected abuse', | ||
| ); | ||
| }); | ||
|
|
||
| it('returns the denied message', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 403, | ||
| json: () => | ||
| Promise.resolve({ | ||
| message: 'user has been denied', | ||
| }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe( | ||
| 'Access to the Developer Sandbox has been denied', | ||
| ); | ||
| }); | ||
|
|
||
| it('returns the admin not allowed message', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 403, | ||
| json: () => | ||
| Promise.resolve({ | ||
| message: 'failed to create usersignup for admin-user', | ||
| }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe( | ||
| 'A CRT admin is not allowed to sign up', | ||
| ); | ||
| }); | ||
|
|
||
| it('returns the already signed up message', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 409, | ||
| json: () => | ||
| Promise.resolve({ | ||
| message: 'there is already an active UserSignup with such a username', | ||
| }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe( | ||
| 'An account is already signed up to Developer Sandbox with your username', | ||
| ); | ||
| }); | ||
|
|
||
| it('returns the default error message for an unrecognized message', async () => { | ||
| const response = createMockResponse({ | ||
| ok: false, | ||
| status: 400, | ||
| json: () => | ||
| Promise.resolve({ | ||
| message: 'some completely unexpected error from the backend', | ||
| }), | ||
| }); | ||
|
|
||
| const userSignupError = await UserSignupError.fromResponse(response); | ||
|
|
||
| expect(userSignupError.message).toBe(defaultErrorMessage); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { CommonResponse } from '../../types'; | ||
|
|
||
| /** | ||
| * Defines the error type that is useful for problems with the user's signup. | ||
| */ | ||
| export default class UserSignupError extends Error { | ||
| constructor(message: string) { | ||
| super(message); | ||
| } | ||
|
|
||
| /** | ||
| * Creates the error from the given response, by attempting to read and set | ||
| * a user friendly error message. | ||
| * @param response the response to process. | ||
| * @returns a user friendly error message. | ||
| */ | ||
| static async fromResponse(response: Response): Promise<UserSignupError> { | ||
| const defaultErrorMessage: string = | ||
| 'Unable to sign you up into Developer Sandbox. Please contact devsandbox@redhat.com'; | ||
|
|
||
| if (response.status === 500) { | ||
| return new UserSignupError(defaultErrorMessage); | ||
| } | ||
|
|
||
| let body: CommonResponse | undefined; | ||
| try { | ||
| body = await response.json(); | ||
| } catch { | ||
| return new UserSignupError(defaultErrorMessage); | ||
| } | ||
|
|
||
| if (!body || !body.message) { | ||
| return new UserSignupError(defaultErrorMessage); | ||
| } | ||
|
|
||
| const { message, details } = body; | ||
| switch (true) { | ||
| case message.includes('invalid code'): { | ||
| const baseErrMessage = 'The provided activation code is invalid'; | ||
| if (details) { | ||
| return new UserSignupError(`${baseErrMessage}: ${details}`); | ||
| } else { | ||
| return new UserSignupError(baseErrMessage); | ||
| } | ||
| } | ||
| case message.includes('has been suspended'): | ||
| return new UserSignupError( | ||
| 'Access to the Developer Sandbox has been suspended due to suspicious activity or detected abuse', | ||
| ); | ||
| case message.includes('has been denied'): | ||
| return new UserSignupError( | ||
| 'Access to the Developer Sandbox has been denied', | ||
| ); | ||
| case message.includes('failed to create usersignup for'): | ||
| return new UserSignupError('A CRT admin is not allowed to sign up'); | ||
| case message.includes( | ||
| 'there is already an active UserSignup with such a username', | ||
| ): | ||
|
Comment on lines
+46
to
+58
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only out of curiosity - couldn't we simply:
instead of matching based on the message content?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about it, but the problem I see is that the registration service might return some non-user friendly error messages containing information that the user doesn't really care about. So if we take just the message or details and post it as an alert in the UI, it might contain stuff like "error creating UserSignup resource", or "error retrieving K8s object Y", or whatever. I personally don't like much either having to parse the error messages, but I think it provides the most user friendly error messages without exposing internals that they do not care or know about. The other option I thought about is modifying the error messages to include some codes that the UI can interpret, or even better, refactor the errors we return from the back end so that they're always user friendly, which I guess it should be. However I didn't think that the effort would be worth it? If you think otherwise, though, I'd be very much willing to refactor it |
||
| return new UserSignupError( | ||
| 'An account is already signed up to Developer Sandbox with your username', | ||
| ); | ||
| default: | ||
| return new UserSignupError(defaultErrorMessage); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.