department-of-veterans-affairs/vets-website

View on GitHub
src/applications/ivc-champva/shared/components/fileUploads/supportingDocsVerification.js

Summary

Maintainability
A
1 hr
Test Coverage
// This class is used by the MissingFileOverview component to verify
// arbitrary "required" documents have been uploaded. Requirement is based
// off of the object provided to SupportingDocsVerification at instantiation.

export default class SupportingDocsVerification {
  /**
   * Constructs this class with the form-specific required files object.
   * @param {object} requiredFiles object mapping required file keynames to
   * human-readable names, e.g.:
   *   {
   *     applicantAdoptionPapers: 'Proof of Adoption',
   *   }
   */
  constructor(requiredFiles) {
    this.requiredFiles = requiredFiles;
  }

  /**
   * Dynamically get list of applicant property names that correspond to a
   * file upload so we can check if a given applicant has uploaded that
   * particular file without having to hardcode the list of file properties.
   * @param {object} pages contains all pages in current form
   * @returns List of objects describing file upload properties in the shape:
   *   [
   *     {name: 'birthCert', path: 'birthcert-upload', required: boolean},
   *   ]
   */
  getApplicantFileKeyNames(pages) {
    return (
      Object.keys(pages)
        // Grab pages in the 'applicants' list and loop
        .filter(page => pages[page]?.arrayPath === 'applicants')
        .map(page => {
          const { items } = pages[page].schema.properties.applicants;
          const itemProps = Array.isArray(items)
            ? items[0]?.properties // Confirmation page has different page structure
            : items?.properties;
          return Object.keys(itemProps ?? {}).map(
            item =>
              // Descend into the page to determine if it has a file upload
              itemProps[item]?.type === 'array'
                ? {
                    name: item,
                    path: pages[page].path,
                    required: Object.keys(this.requiredFiles).includes(item),
                  }
                : undefined,
          );
        })
        .flat(Infinity) // List of all potential files
        .filter(el => el) // Remove undefined values
    );
  }

  /**
   * Dynamically get list of sponsor property names that correspond to a file
   * upload.
   * @param {object} pages contains all pages in current form
   * @returns List of objects describing file upload properties in the shape:
   *   [
   *     {name: 'birthCert', path: 'birthcert-upload', required: boolean},
   *   ]
   */
  getSponsorFileKeyNames(pages) {
    return Object.keys(pages)
      .map(page =>
        Object.keys(pages[page].schema.properties)
          .filter(
            key =>
              pages[page].schema.properties[key].type === 'array' &&
              key !== 'applicants',
          )
          .map(el => {
            return {
              name: el,
              path: pages[page].path,
              required: Object.keys(this.requiredFiles).includes(el),
            };
          }),
      )
      .flat(Infinity);
  }

  /**
   * Return list of files user still needs to upload based on current form state
   * @param {object} pages contains all pages in current form
   * @param {object} person either an applicant or the sponsor object
   * @param {boolean} isSponsor whether or not person is the Sponsor
   * @returns List of file-upload properties where no file has been uploaded yet
   */
  identifyMissingUploads(pages, person, isSponsor) {
    // Get list of possible files (e.g., for sponsor ['dischargePapers', ...])
    const possibleFiles = isSponsor
      ? this.getSponsorFileKeyNames(pages)
      : this.getApplicantFileKeyNames(pages);
    // Filter down to just files that haven't been uploaded yet
    return possibleFiles.filter(file => person[file.name] === undefined);
  }
}