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?
Select a repo