Moving a Form to Production

Migrating Forms into Vets-Json-Schema

Once you've tested your application thoroughly and want to move it into production, you'll need to update the schema in the Vets-Json-Schema Repo.

This repository is used to store the JSON schema for data contract validation between vets-website and vets-api. You must follow the instructions in the repository to update the correct JSON schema for your form. (ex. 10-10CG, 10-10EZ, 22-1990, etc).

Here is a helpful link for creating a new JSON Schema in vets-json-schema: Creating a JSON Schema in Vets-JSON-Schema.

This is for adding to the vets-json-schema which has to be done for the form to work in production.

When working locally you don’t need to add this since the schema hasn’t been migrated to vets-json-schema yet.

Making this change is crucial if you're adding new schema definitions to your app or if you're creating a brand new form that does not have a matching OMB number (or any OMB number at all).

NOTE: When creating the pull request (PR) for vets-json-schema, you need to remember to bump the version number in the package.json file, as shown on line 3 in this example:

{
  "name": "vets-json-schema",
  "version": "20.19.0",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/department-of-veterans-affairs/vets-json-schema.git"
  },
  ...
}
JSON

Common Schema Reminder

When developing code in vets-website it may be tempting to create constants inside of your applications /config directory, but we suggest to use the vets-json-schema common definitions as much as possible before creating custom constants.

Updating the vets-website package.json Dependency

After you submit your PR to vets-json-schema and it's accepted and merged into the main branch, we need to move on to Vets Website. Copy the commit sha from the vets-json-schema merge and move into the package.json file. Find the key vets-json-schema, which should be in the dependencies section; this needs to be updated with the commit sha that was copied.

"dependencies": {
    ...
    "vets-json-schema": "https://github.com/department-of-veterans-affairs/vets-json-schema.git#5c0af7a98ab0390566f4756428c90625ff145b31",
    ...
  }
JSON

Create a new PR for vets-website with the included fix to the package.json file (including the new commit sha from the vets-json-schema in the above step) and all of your application changes, such as form updates, config updates, styling updates, etc.

vets-api Gemfile.lock Entry

One more step and everything will be up and running! Next step is to navigate to Vets API, where the Gemfile.lock file needs to be updated to take the new commit sha that was copied from the vets-json-schema PR/Merge.

GIT
  remote: https://github.com/department-of-veterans-affairs/vets-json-schema
  revision: 5c0af7a98ab0390566f4756428c90625ff145b31
  branch: master
  specs:
    vets_json_schema (20.19.0)
      multi_json (~> 1.0)
      script_utils (= 0.0.4)
CODE

After making this change, submit a new PR in the vets-api repository and now everything should be linked up and all schemas will validate properly in the production site.

Managing Forms in Production

formConfig Migrations Attribute

Once a form is in production and using the save in progress functionality, there is a dilemma when you want to change some fields or some validation on the form. Since there are instances of the form that have been saved, if you change the structure of the form the saved data may no longer match and result in confusing validation errors for a user trying to fill out the form. Similarly, if validation changes, a user might have already gone past that form field and won't know how to fix the new error.

To address this issue, you can write "migrations" for form data. This way, you can update a user's data to match the new structure, as well as move their position in the form back, if they need to adjust something. Migrations are functions, run in a sequence, based on the "version" of the form, which is saved along with the form data.

Migrations go in the formConfig object:

const formConfig = {
   ...
   version: 1,
   migrations: [
     ({ formData, metadata, formId }) => {
       // do something
       return {
         formData,
         metadata
       };
     }
   ]
   ...
};
JS

We typically start at version 0 and increment each time we need to add a migration. Migrations are functions that get formData, metadata, and formIdas arguments, and they must return the formData and metadata with any changes that they’ve made. For example, here is a migration that the health care application uses to convert an old url to migration.js:

({ formData, metadata }) => {
  const url = metadata.returnUrl || metadata.return_url;
  let newMetadata = metadata;

  if (url === '/household-information/child-information') {
    newMetadata = _.set('returnUrl', '/household-information/dependent-information', metadata);
  }

  return { formData, metadata: newMetadata };
}
JS

If the current return URL (which is the URL a user will return to when they load a saved form) matches the outdated one (/child-information), we update it to /dependent-information. The formDatais not changed in this case.

Another example from the health care application was to fix a bug in the form structure:

(savedData) => {
  const newData = savedData;
  newData.formData = _.unset('isSpanishHispanicLatino', savedData.formData);

  if (typeof _.get('view:demographicCategories.isSpanishHispanicLatino', newData.formData) === 'undefined') {
    newData.formData = _.set('view:demographicCategories.isSpanishHispanicLatino', false, newData.formData);
    return newData;
  }

  return newData;
}
JS

In this example, we're moving an incorrectly located isSpanishHispanicLatinoflag and setting it in the right spot.

Writing migrations is not very common, but you may need to do it if you modify a form that's already in production.

NOTE: Below are more examples of migrations that could be useful for reference:

  1. src/applications/pensions/migrations.js

({ formData, metadata }) => {
    // First place is active service period
    if (formData.servicePeriods) {
      const fromDate = convertToDateField(formData.veteranDateOfBirth);
      const dateError = formData.servicePeriods.some(period => {
        const toDate = convertToDateField(
          get('activeServiceDateRange.from', period),
        );

        return !isValidDateRange(fromDate, toDate);
      });

      if (dateError) {
        return {
          formData,
          metadata: set('returnUrl', '/military/history', metadata),
        };
      }
    }

    if (formData.marriages) {
      const index = formData.marriages.findIndex(marriage => {
        const fromDate = convertToDateField(marriage.dateOfMarriage);
        const toDate = convertToDateField(
          get('view:pastMarriage.dateOfSeparation', marriage),
        );

        return !isValidDateRange(fromDate, toDate);
      });

      if (index >= 0) {
        return {
          formData,
          metadata: set('returnUrl', `/household/marriages/${index}`, metadata),
        };
      }
    }
JS

2. src/applications/burials/migrations.js


export default [
  // 0 > 1, move user back to address page if zip code is bad
  ({ formData, metadata }) => {
    let newMetadata = metadata;

    if (
      formData.claimantAddress &&
      !isValidCentralMailPostalCode(formData.claimantAddress)
    ) {
      newMetadata = Object.assign({}, metadata, {
        returnUrl: '/claimant-contact-information',    Erik P. Hansen, 10/02/18 • Prettier ESLint rules (#8571)
      });
    }

    return { formData, metadata: newMetadata };
  },
  // 1 > 2, move user back to file number if incorrect
  ({ formData, metadata }) => {
    const fileNumbeRegex = /^\d{8,9}$/;
    let newMetadata = metadata;

    if (formData.vaFileNumber && !fileNumbeRegex.test(formData.vaFileNumber)) {
      newMetadata = Object.assign({}, metadata, {
        returnUrl: '/veteran-information',
      });
    }

    return { formData, metadata: newMetadata };
  },
];
JS