owned this note
owned this note
Published
Linked with GitHub
# Error Formatting in Caseflow
Initially this will be scoped to just the hearings codebase so that it can be expanded upon in other parts of the codebase in the future. We can begin by determining the current structure of errors and agreeing on a single structure to be applied. The next step could be creating a new component to handle the updated error structure. When the component is in place, we can then begin going through screens/controllers individually and updating the existing handling of errors to use the new component and structure.
Outlined below is a list of the controllers and frontend components involved with sending/displaying errors to the user.
## Frontend
The components below are grouped by the current hearings screens in order to better understand the flow of errors within the frontend and show why different components might be used for different screens based on the types of errors being handled.
**Current Structure**
All of the hearings frontend components that are not shared with other Caseflow apps reside in: `client/app/hearings`. The entry point for the Hearings app is: `client/app/hearings/HearingsApp.jsx`. This component contains the browser router and all of the screens within Hearings. Below are the current list of screens:
- HearingWorksheetPrintAllContainer: `/hearings/worksheet/print`
- UnsupportedBrowserBanner (loaded when browser not chrome)
- StatusMessage
- Static `type/title/messageText`
- LoadingDataDisplay
- HearingWorksheetPrinted
- WorksheetFooter
- WorksheetHeader
- DailyDocketContainer:
**Routes**
- `/hearings/docket/:hearingDayId/print`
- `/hearings/schedule/docket/:hearingDayId`
**State**
- `serverError`
- Boolean
- Set by `HANDLE_DAILY_DOCKET_SERVER_ERROR` action
- `dailyDocketServerError` (redux)
- Boolean
- Set by `HANDLE_DAILY_DOCKET_SERVER_ERROR` action
- `onErrorHearingDayLock` (redux)
- Boolean
- Set by `HANDLE_LOCK_HEARING_SERVER_ERROR` action
**Components**
- LoadingDataDisplay
- Alert `client/app/components/Alert.jsx`
- `message` or `children`
- `title`
- `type` one of `success`, `error`, `warning`, or `info`
- DailyDocketPrinted
- DailyDocket
- UserAlerts `client/app/components/UserAlerts.jsx`
**Props**
- `alerts`
- Array of Objects
- Set by props
- Objects contain keys that match the Alert component
**Components**
- Alert `client/app/components/Alert.jsx`
- Alert `client/app/components/Alert.jsx`
- 3 instances all with hard-coded values
- DispositionModal
- RemoveHearingModal
- LockModal
- DailyDocketEditLinks
- DailyDocketHearingRows
- DailyDocketRow
**State**
- `alerts`
- Array of Objects
- Set by `RECEIVE_TRANSITIONING_ALERT`, `TRANSITION_ALERT`, `REMOVE_ALERTS_WITH_EXPIRATION`, `CLEAR_ALERTS`, or `RECEIVE_ALERTS`
- `client/app/components/common/reducers.js`
- `transitioningAlerts`
- Array of Objects
- Set by `RECEIVE_TRANSITIONING_ALERT` or `TRANSITION_ALERT`
- `client/app/components/common/reducers.js`
**Components**
- HearingText `client/app/hearings/components/dailyDocket/DailyDocketRowDisplayText.jsx`
- PreppedCheckbox `client/app/hearings/components/dailyDocket/DailyDocketRowInputs.jsx`
- DocketTypeBadge
- PowerOfAttorneyName `client/app/queue/PowerOfAttorneyDetail.jsx`
- HearingTime `client/app/hearings/components/HearingTime.jsx`
- HearingTime `client/app/hearings/components/modalForms/HearingTime.jsx`
- `errorMessage` string is set by props, however does not appear to be set by the `DailyDocketRow`
- VirtualHearingModal
**State**
- `representativeEmailError`
- String
- set bythe catch block of `onSubmit`
- Value always set to `INVALID_EMAIL_FORMAT` constant
- `appellantEmailError`
- String
- set by `setAppellantEmailError` in `validateForm` and in the catch block of `onSubmit`
- Value always set to `INVALID_EMAIL_FORMAT` constant
- AodModal
**client/app/hearings/components/dailyDocket/DailyDocketRowInputs.jsx**
- StaticHearingDay
- HoldOpenDropdown
- StaticVirtualHearing
- DispositionDropdown
- Waive90DayHoldCheckbox
- TranscriptRequestedCheckbox
- HearingDetailsLink
- LegacyAodDropdown
- AmaAodDropdown
- AodReasonDropdown
- HearingPrepWorkSheetLink
- StaticRegionalOffice
- NotesField
- HearingLocationDropdown
- StatusMessage
- `title` set by `COPY.HEARING_SCHEDULE_DOCKET_JUDGE_WITH_NO_HEARINGS` or `COPY.HEARING_SCHEDULE_DOCKET_NO_VETERANS`
- HearingDayEditModal
- HearingRoomDropdown
- `errorMessage` string set by props, not used in `HearingDayEditModal`
- JudgeDropdown
- `errorMessage` string set by props, not used in `HearingDayEditModal`
- HearingCoordinatorDropdown
- `errorMessage` string set by props, not used in `HearingDayEditModal`
- HearingsUserContext: `/hearings/:hearingId/details`
- HearingDetailsContainer
- LoadingDataDisplay
- HearingsFormContextProvider
- Snapshot of hearings state, does not contain error state
- HearingDetails
- Catches errors in `submit`
- Mapped to `error.message` instead of `error.detail`
**State**
- `virtualHearingErrors`
- Object
- Set by `submit function` checks and catch block
- Maps to `errors` object in lower components
- `error`
- String
- Set by
- `resetState`
- `submit` function catch block
**Components**
- UserAlerts `client/app/components/UserAlerts.jsx`
- Alert `client/app/components/Alert.jsx`
- HearingConversion
- HearingTime
- AppellantSection
- RepresentativeSection
- VirtualHearingSection
- DetailsHeader
- TitleDetailsSubheader
- TitleDetailsSubheaderSection
- DocketTypeBadge
- DetailsForm
- JudgeDropdown
- HearingCoordinatorDropdown
- HearingRoomDropdown
- HearingTypeDropdown
- VirtualHearingForm
- HearingLinks
- VirtualHearingFields
- Timezone
- VirtualHearingEmail
- HelperText
- EmailNotificationHistory
- TranscriptionFormSection
- TranscriptionDetailsInputs
- VirtualHearingModal
- Catches errors in `onSubmit`
- Mapped to `error.message` instead of `error.detail`
**State**
- `representativeEmailError`
- String
- set bythe catch block of `onSubmit`
- Value always set to `INVALID_EMAIL_FORMAT` constant
- `appellantEmailError`
- String
- set by `setAppellantEmailError` in `validateForm` and in the catch block of `onSubmit`
- Value always set to `INVALID_EMAIL_FORMAT` constant
- HearingWorksheetContainer: `/hearings/:hearingId/worksheet`
**NOTE**: Did not find any errors outside of local errors
- LoadingDataDisplay
- HearingWorksheet
- HearingWorksheetDocs
- HearingWorksheetStream
- HearingWorksheetIssues
- HearingWorksheetIssueFields
- HearingWorksheetPreImpressions
- HearingWorksheetIssueDelete
- WorksheetHeaderVeteranSelection
- WorksheetHeader `client/app/hearings/components/hearingWorksheet/WorksheetHeader.jsx`
- DocketTypeBadge
- WorksheetFormEntry `client/app/hearings/components/hearingWorksheet/WorksheetHeader.jsx`
- WorksheetFormEntry `client/app/hearings/components/hearingWorksheet/WorksheetHeader.jsx`
- ListScheduleContainer: `/hearings/schedule`
**State**
- `serverError`
- Boolean
- Never set or used
**Components**
- QueueCaseSearchBar
- CaseListSearch
- CaseSearchErrorMessage
- `errorType`, `queryResultingInError`, and `errorMessage` set by `caseList.search` redux state
- Alert
- Alert (no errors displayed)
- Alert (Second instance shows error based on `invalidDates` key through props from `hearingSchedule` redux state)
- ListSchedule
- ListTable
- LoadingDataDisplay
- QueueTable
- ListScheduleDateSearch
- HearingDayAddModal
**State**
- `errorMessages`
- Array of Strings
- Set by `onClickConfirm`
- `roErrorMessages`
- Array of Strings
- Set by `onClickConfirm`
- `dateError`
- Boolean
- Set by `onClickConfirm`, `resetErrorState`
- Pushes to `errorMessages` local variable
- `typeError`
- Boolean
- Set by `onClickConfirm`, `resetErrorState`
- Pushes to `errorMessages` local variable
- `roError`
- Boolean
- Set by `onClickConfirm`, `resetErrorState`
- Pushes to `roErrorMessages` local variable
- `serverError`
- Boolean
- Set by `persistHearingDay`
- `noRoomsAvailableError`
- Boolean
- Set by `persistHearingDay`
- Maps to `error.detail` instead of `error.details`
**Components**
- Alert (Displays errors only for `serverError` and `noRoomsAvailableError`)
- RegionalOfficeDropdown
- JudgeDropdown
- HearingCoordinatorDropdown
- `errorMessage` received through props
- BuildScheduleContainer: `/hearings/schedule/build`
- BuildSchedule
- Alert (Displays only success messages)
- BuildScheduleUploadContainer: `/hearings/schedule/build/upload`
**State**
- All errors set by
- `createSchedulePeriod`
- Calls POST '/hearings/schedule_periods' endpoint
- `validateData`
- `uploadFormErrors`
- Set by `hearingSchedule` redux state
- `uploadRoCoFormErrors`
- Set by `hearingSchedule` redux state
- `uploadJudgeFormErrors`
- Set by `hearingSchedule` redux state
**Components**
- BuildScheduleUpload
- `uploadJudgeFormErrors`, `uploadRoCoFormErrors`
- String
- Removes "Validation failed: " string
- Splits and maps to individual strings on commas
- `uploadFormErrors` set on RadioField component
- ReviewAssignmentsContainer: `/hearings/schedule/build/upload/:schedulePeriodId`
**State**
- `schedulePeriodError`
- Set by redux state `schedulePeriodError`
- Calls GET '/hearings/schedule_periods/:schedulePeriodId' endpoint
- `spErrorDetails`
- Set by redux state `spErrorDetails`
- Maps to `error.details` instead of `error.detail`
**Components**
- ReviewAssignments
- Alert
- StatusMessage
- AssignHearingsContainer: `/hearings/schedule/assign`
- RegionalOfficeDropdown
- LoadingDataDisplay
- AssignHearings
- NoUpcomingHearingDayMessage
- StatusMessage
- AssignHearingsTabs
- UpcomingHearingsTable
- LinkToAppeal
- HearingAppellantName
- HearingDocketTag
- HearingTime
- QueueTable
- AssignHearingsTable
**State**
- `showNoVeteransToAssignError`
- Boolean
- Set by `onPageLoaded`
**Components**
- NoVeteransToAssignMessage
- StatusMessage
- QueueTable
## Backend
The controllers below each contain some response with a defined error structure. They can be grouped based on how the error is currently being constructed and sent to the frontend.
**Current Structure**
- HearingsController `app/controllers/hearings_controller.rb`
- all
- `ActiveRecord::RecordNotFound` (`errors` [`message`, `code`])
- `ActiveRecord::RecordNotUnique` (`errors` [`message`, `code`])
- `ActiveRecord::RecordInvalid` (`errors` [`message`, `code`])
- `Caseflow::Error::VacolsRepositoryError` (`errors` [`message`, `code`])
- find_closest_hearing_locations
- no error class "facility response error"
- defined in `ExternalApi::VADotGovService`, serialized in `Caseflow::Error`
- `Caseflow::Error::VaDotGovMissingFacilityError` (`errors` [{ `status`, `title`, `detail`}])
- defined in `ExternalApi::VADotGovService::Response`, serialized in `Caseflow::Error`
- `Caseflow::Error::VaDotGovLimitError` (`errors` [{ `status`, `title`, `detail`}])
- `Caseflow::Error::VaDotGovRequestError` (`errors` [{ `status`, `title`, `detail`}])
- `Caseflow::Error::VaDotGovServerError` (`errors` [{ `status`, `title`, `detail`}])
- Hearings::ScheduleHearingTasksColumnsController `app/controllers/hearings/schedule_hearing_tasks_columns_controller.rb`
- appeal_type
- `Caseflow::Error::InvalidParameter` (`parameter`, `message`)
- Hearings::SchedulePeriodsController `app/controllers/hearings/schedule_periods_controller.rb`
- show
- `HearingSchedule::Errors::NotEnoughAvailableDays` (`error`, `details`, `type`)
- `HearingSchedule::Errors::CannotAssignJudges` (`error`, `details`, `type`)
- create
- `ActiveRecord::RecordInvalid` (`error`)
- update
- no error class "schedule period cannot be finalized" (`error`)
- Hearings::HearingDayController `app/controllers/hearings/hearing_day_controller.rb`
- create
- `ActiveRecord::RecordInvalid` (`errors`: [`title`, `detail`])
- no error class "no available rooms" (`errors`: [`title`, `detail`, `status`])
- index, index_with_hearings
- no error class "hearing day range invalid" (`errors`: [`title`, `details`])
## Risks/Unknowns
- Potentially a large number of controllers involved with a single screen
- Could cause challenges with implementation if some controllers have different error structure than others.
- How dependent on the redux state is the error handling currently?
- How much of the error handling resides in component lifecycle methods currently?
- Are there places where the hearings app receives error messages from an endpoint outside of the core hearings app controllers and if so how does the error handling differ there?