diff --git a/frontend/kolide/index.tests.js b/frontend/kolide/index.tests.js index f08e1aca6d..0871174142 100644 --- a/frontend/kolide/index.tests.js +++ b/frontend/kolide/index.tests.js @@ -595,9 +595,12 @@ describe('Kolide - API client', () => { }); it('return errors correctly for unsuccessful requests', (done) => { - const error = 'Resource not found'; + const errorResponse = { + message: 'Resource not found', + errors: [{ name: 'base', reason: 'Resource not found' }], + }; const passwordResetToken = 'invalid-password-reset-token'; - const request = invalidResetPasswordRequest(newPassword, passwordResetToken, error); + const request = invalidResetPasswordRequest(newPassword, passwordResetToken, errorResponse); const formData = { new_password: newPassword, password_reset_token: passwordResetToken, @@ -605,10 +608,7 @@ describe('Kolide - API client', () => { Kolide.resetPassword(formData) .then(done) - .catch((errorResponse) => { - const { response } = errorResponse; - - expect(response).toEqual({ error }); + .catch(() => { expect(request.isDone()).toEqual(true); done(); }); diff --git a/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx b/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx index ad721f5933..dfb3137f0a 100644 --- a/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx +++ b/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx @@ -1,18 +1,22 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { noop } from 'lodash'; +import { noop, size } from 'lodash'; import { push } from 'react-router-redux'; -import debounce from '../../utilities/debounce'; -import { resetPassword } from '../../redux/nodes/components/ResetPasswordPage/actions'; -import ResetPasswordForm from '../../components/forms/ResetPasswordForm'; -import StackedWhiteBoxes from '../../components/StackedWhiteBoxes'; -import { performRequiredPasswordReset } from '../../redux/nodes/auth/actions'; -import userInterface from '../../interfaces/user'; +import debounce from 'utilities/debounce'; +import { clearResetPasswordErrors, resetPassword } from 'redux/nodes/components/ResetPasswordPage/actions'; +import ResetPasswordForm from 'components/forms/ResetPasswordForm'; +import StackedWhiteBoxes from 'components/StackedWhiteBoxes'; +import { performRequiredPasswordReset } from 'redux/nodes/auth/actions'; +import userInterface from 'interfaces/user'; export class ResetPasswordPage extends Component { static propTypes = { dispatch: PropTypes.func, + errors: PropTypes.shape({ + base: PropTypes.string, + new_password: PropTypes.string, + }), token: PropTypes.string, user: userInterface, }; @@ -31,6 +35,16 @@ export class ResetPasswordPage extends Component { return false; } + onResetErrors = () => { + const { dispatch, errors } = this.props; + + if (size(errors)) { + dispatch(clearResetPasswordErrors); + } + + return false; + } + onSubmit = debounce((formData) => { const { dispatch, token, user } = this.props; @@ -46,7 +60,8 @@ export class ResetPasswordPage extends Component { return dispatch(resetPassword(resetPasswordData)) .then(() => { return dispatch(push('/login')); - }); + }) + .catch(() => false); }) handleLeave = (location) => { @@ -61,11 +76,13 @@ export class ResetPasswordPage extends Component { const passwordUpdateParams = { password }; return dispatch(performRequiredPasswordReset(passwordUpdateParams)) - .then(() => { return dispatch(push('/')); }); + .then(() => { return dispatch(push('/')); }) + .catch(() => false); } render () { - const { handleLeave, onSubmit } = this; + const { handleLeave, onResetErrors, onSubmit } = this; + const { errors } = this.props; return ( - + ); } diff --git a/frontend/redux/nodes/components/ResetPasswordPage/actions.js b/frontend/redux/nodes/components/ResetPasswordPage/actions.js index d5c37ad2a7..0606ca2a18 100644 --- a/frontend/redux/nodes/components/ResetPasswordPage/actions.js +++ b/frontend/redux/nodes/components/ResetPasswordPage/actions.js @@ -1,4 +1,5 @@ -import Kolide from '../../../../kolide'; +import { formatErrorResponse } from 'redux/nodes/entities/base/helpers'; +import Kolide from 'kolide'; export const CLEAR_RESET_PASSWORD_ERRORS = 'CLEAR_RESET_PASSWORD_ERRORS'; export const RESET_PASSWORD_ERROR = 'RESET_PASSWORD_ERROR'; @@ -6,11 +7,11 @@ export const RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST'; export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; export const clearResetPasswordErrors = { type: CLEAR_RESET_PASSWORD_ERRORS }; -export const resetPasswordError = (error) => { +export const resetPasswordError = (errors) => { return { type: RESET_PASSWORD_ERROR, payload: { - error, + errors, }, }; }; @@ -27,9 +28,10 @@ export const resetPassword = (formData) => { return dispatch(resetPasswordSuccess); }) .catch((response) => { - const { error } = response; + const errorsObject = formatErrorResponse(response); + + dispatch(resetPasswordError(errorsObject)); - dispatch(resetPasswordError(error)); throw response; }); }; diff --git a/frontend/redux/nodes/components/ResetPasswordPage/reducer.js b/frontend/redux/nodes/components/ResetPasswordPage/reducer.js index c186e708e3..e366e7d777 100644 --- a/frontend/redux/nodes/components/ResetPasswordPage/reducer.js +++ b/frontend/redux/nodes/components/ResetPasswordPage/reducer.js @@ -6,7 +6,7 @@ import { } from './actions'; export const initialState = { - error: null, + errors: {}, loading: false, }; @@ -15,12 +15,12 @@ export default (state = initialState, { type, payload }) => { case CLEAR_RESET_PASSWORD_ERRORS: return { ...state, - error: null, + errors: {}, }; case RESET_PASSWORD_ERROR: return { ...state, - error: payload.error, + errors: payload.errors, loading: false, }; case RESET_PASSWORD_REQUEST: @@ -31,7 +31,7 @@ export default (state = initialState, { type, payload }) => { case RESET_PASSWORD_SUCCESS: return { ...state, - error: null, + errors: {}, loading: false, }; default: diff --git a/frontend/redux/nodes/components/ResetPasswordPage/reducer.tests.js b/frontend/redux/nodes/components/ResetPasswordPage/reducer.tests.js index a9454cae5b..d5154d1110 100644 --- a/frontend/redux/nodes/components/ResetPasswordPage/reducer.tests.js +++ b/frontend/redux/nodes/components/ResetPasswordPage/reducer.tests.js @@ -22,12 +22,12 @@ describe('ResetPasswordPage - reducer', () => { it('changes the loading state to true', () => { const errorState = { ...initialState, - error: 'Something went wrong', + errors: { base: 'Something went wrong' }, }; expect(reducer(errorState, clearResetPasswordErrors)).toEqual({ ...errorState, - error: null, + errors: {}, }); }); }); @@ -45,23 +45,23 @@ describe('ResetPasswordPage - reducer', () => { it('changes the loading state to false and errors to null', () => { const loadingStateWithError = { loading: true, - error: 'Something went wrong', + errors: { base: 'Something went wrong' }, }; expect(reducer(loadingStateWithError, resetPasswordSuccess)).toEqual({ loading: false, - error: null, + errors: {}, }); }); }); describe('resetPasswordError', () => { it('changes the loading state to false and sets the error state', () => { - const error = 'There was an error with your request'; + const errors = { base: 'There was an error with your request' }; - expect(reducer(initialState, resetPasswordError(error))).toEqual({ + expect(reducer(initialState, resetPasswordError(errors))).toEqual({ ...initialState, - error, + errors, loading: false, }); }); @@ -98,18 +98,23 @@ describe('ResetPasswordPage - reducer', () => { new_password: newPassword, password_reset_token: token, }; - const error = 'Something went wrong'; - const invalidRequest = invalidResetPasswordRequest(newPassword, token, error); + const errors = [{ name: 'base', reason: 'Something went wrong' }]; + const errorResponse = { + status: 422, + message: 'Something went wrong', + errors, + }; + const invalidRequest = invalidResetPasswordRequest(newPassword, token, errorResponse); const store = reduxMockStore(); store.dispatch(resetPassword(formData)) .then(done) - .catch((errorResponse) => { + .catch(() => { const actions = store.getActions(); - const { response } = errorResponse; - - expect(response).toEqual({ error }); - expect(actions).toInclude(resetPasswordError(error)); + expect(actions).toInclude(resetPasswordError({ + base: 'Something went wrong', + http_status: 422, + })); expect(invalidRequest.isDone()).toEqual(true); done(); }); diff --git a/frontend/test/mocks.js b/frontend/test/mocks.js index 6f9731af8e..d8011be22c 100644 --- a/frontend/test/mocks.js +++ b/frontend/test/mocks.js @@ -347,13 +347,13 @@ export const validRevokeInviteRequest = (bearerToken, invite) => { .reply(200, {}); }; -export const invalidResetPasswordRequest = (password, token, error) => { +export const invalidResetPasswordRequest = (password, token, errorResponse) => { return nock('http://localhost:8080') .post('/api/v1/kolide/reset_password', JSON.stringify({ new_password: password, password_reset_token: token, })) - .reply(422, { error }); + .reply(422, errorResponse); }; export const validRunQueryRequest = (bearerToken, data) => {