Skip to content

Commit

Permalink
Merge pull request #61 from LaunchPadLab/ch-error-mapping
Browse files Browse the repository at this point in the history
Enhance robustness of error mapping
  • Loading branch information
chawes13 authored Sep 18, 2019
2 parents 328f95d + 5bdca4e commit 42009c2
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 12 deletions.
2 changes: 1 addition & 1 deletion docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A wrapper around the `reduxForm` HOC exported from
[redux-form](https://www.npmjs.com/package/redux-form) that gives it some extra functionality:

1. Makes extra options available for configuring the form
2. Wraps every `onSubmit` result in a promise. Additionally, wraps rejected `onSubmit` results in a `SubmissionError`. If the thrown error has an `errors` property, its value will be passed to `SubmissionError`. The original error will be accessible via the `SubmissionError`s `meta.error` property. This enables developers to access useful information regarding the origin of the failure, e.g., HTTP status.
2. Wraps every `onSubmit` result in a promise. Additionally, wraps rejected `onSubmit` results in a `SubmissionError`. If the thrown error has an `errors` property, its value will be passed to `SubmissionError`. Else, if the thrown error has a `message` property, this will be passed to a `SubmissionError` as a form-wide error. The original error will be accessible via the `SubmissionError`s `meta.error` property. This enables developers to access useful information regarding the origin of the failure, e.g., HTTP status.
3. Provides a default `onSubmit` function that resolves successfully and logs a warning.
4. Ignores any `onChange` events that occur on a pristine and untouched form, patching a bug in `redux-form v8`.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@launchpadlab/lp-form",
"version": "2.9.0",
"version": "2.10.0",
"description": "Extensions for the reduxForm HOC",
"main": "lib/index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/lpForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
* {@link https://www.npmjs.com/package/redux-form|redux-form} that gives it some extra functionality:
*
* 1. Makes extra options available for configuring the form
* 2. Wraps every `onSubmit` result in a promise. Additionally, wraps rejected `onSubmit` results in a `SubmissionError`. If the thrown error has an `errors` property, its value will be passed to `SubmissionError`. The original error will be accessible via the `SubmissionError`s `meta.error` property. This enables developers to access useful information regarding the origin of the failure, e.g., HTTP status.
* 2. Wraps every `onSubmit` result in a promise. Additionally, wraps rejected `onSubmit` results in a `SubmissionError`. If the thrown error has an `errors` property, its value will be passed to `SubmissionError`. Else, if the thrown error has a `message` property, this will be passed to a `SubmissionError` as a form-wide error. The original error will be accessible via the `SubmissionError`s `meta.error` property. This enables developers to access useful information regarding the origin of the failure, e.g., HTTP status.
* 3. Provides a default `onSubmit` function that resolves successfully and logs a warning.
* 4. Ignores any `onChange` events that occur on a pristine and untouched form, patching a bug in `redux-form v8`.
*
Expand Down
32 changes: 23 additions & 9 deletions src/middleware/wrapSubmissionPromise.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { withPropsOnChange } from 'recompose'
import { SubmissionError } from 'redux-form'
import isPromise from 'is-promise'
import { getOr } from 'lodash/fp'
import { get } from 'lodash/fp'

// Wrap submission results in a redux-form SubmissionError.
// Also ensures that the return value of onSubmit is a promise.
Expand All @@ -12,17 +12,31 @@ const wrapSubmissionPromise = withPropsOnChange(
onSubmit: (...args) => {
const result = onSubmit(...args)
if (!isPromise(result)) return Promise.resolve(result)
return result.catch(err => {
const messages = getOr({}, 'errors', err)
const submissionError = new SubmissionError(messages)

// Retain metadata (e.g., status code) about the original error
submissionError.meta = { error: err }
throw submissionError
})
return result.catch(wrapSubmissionError)
}
}
}
)

// Attempts to grab the errors or error message provided and wraps it in a redux-form SubmissionError
// The original error is stored in the error's `meta` key to retain metadata (e.g., status code)
function wrapSubmissionError (error) {
const messages = getErrorMessages(error)
const submissionError = new SubmissionError(messages)

submissionError.meta = { error }
throw submissionError
}

// Checks for an error that matches LPL's default standard for mapping error messages (HttpError).
// Else defaults to the standard error API and maps to a redux-form "form-wide" error key.
function getErrorMessages (err) {
const messages = get('errors', err)

if (messages) return messages

const formWideError = get('message', err)
return formWideError ? { _error: formWideError } : {}
}

export default wrapSubmissionPromise
33 changes: 33 additions & 0 deletions test/lpForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,39 @@ test('lpForm: retains information about the originating error during submit', ()
})
})

test('lpForm: maps generic error messages to form-wide errors', () => {
expect.assertions(1)
const ERROR = "Unprocessable Entity"
const onSubmit = () => {
const error = new Error("Unprocessable Entity")
return Promise.reject(error)
}
const Wrapped = () => <div>Hi</div>
const Form = lpForm({ onSubmit })(Wrapped)
const wrapper = mountWithProvider(<Form />)
const formConfig = wrapper.find(Wrapped).props()

return formConfig.onSubmit(INITIAL_VALUES).catch(e => {
expect(e.errors._error).toEqual(ERROR)
})
})

test('lpForm: returns empty errors if an error message cannot be identified', () => {
expect.assertions(1)
const onSubmit = () => {
const error = new Error()
return Promise.reject(error)
}
const Wrapped = () => <div>Hi</div>
const Form = lpForm({ onSubmit })(Wrapped)
const wrapper = mountWithProvider(<Form />)
const formConfig = wrapper.find(Wrapped).props()

return formConfig.onSubmit(INITIAL_VALUES).catch(e => {
expect(e.errors).toEqual({})
})
})

test('lpForm: creates submitting onChange if submitOnChange is true', () => {
const onChange = jest.fn()
const submit = jest.fn()
Expand Down

0 comments on commit 42009c2

Please sign in to comment.