Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VATEAM-88634: Create a normalization layer for Digital Forms #2215

Conversation

derekhouck
Copy link
Contributor

@derekhouck derekhouck commented Jul 31, 2024

Summary

Normalizes how a Digital Form object looks for post-processing.

Before

{
  "data": {
    "nodeQuery": {
      "entities": [
        {
          "nid": 71159,
          "entityLabel": "Form with One Step",
          "fieldVaFormNumber": "1111111",
          "fieldOmbNumber": "1111-1111",
          "fieldChapters": [
            {
              "entity": {
                "entityId": "158252",
                "type": {
                  "entity": {
                    "entityId": "digital_form_name_and_date_of_bi",
                    "entityLabel": "Name and Date of Birth"
                  }
                },
                "fieldTitle": "The Only Step",
                "fieldIncludeDateOfBirth": true
              }
            }
          ]
        },
        {
          "nid": 71160,
          "entityLabel": "Form with Two Steps",
          "fieldVaFormNumber": "2121212",
          "fieldOmbNumber": "1212-1212",
          "fieldChapters": [
            {
              "entity": {
                "entityId": "158253",
                "type": {
                  "entity": {
                    "entityId": "digital_form_name_and_date_of_bi",
                    "entityLabel": "Name and Date of Birth"
                  }
                },
                "fieldTitle": "First Step",
                "fieldIncludeDateOfBirth": true
              }
            },
            {
              "entity": {
                "entityId": "158254",
                "type": {
                  "entity": {
                    "entityId": "digital_form_name_and_date_of_bi",
                    "entityLabel": "Name and Date of Birth"
                  }
                },
                "fieldTitle": "Second Step",
                "fieldIncludeDateOfBirth": false
              }
            }
          ]
        }
      ]
    }
  }
}

After

[
  {
    "cmsId": 71159,
    "title": "Form with One Step",
    "formId": "1111111",
    "ombNumber": "1111-1111",
    "chapters": [
      {
        "id": 158252,
        "chapterTitle": "The Only Step",
        "type": "digital_form_name_and_date_of_bi",
        "pageTitle": "Name and Date of Birth",
        "additionalFields": {
          "includeDateOfBirth": true
        }
      }
    ]
  },
  {
    "cmsId": 71160,
    "title": "Form with Two Steps",
    "formId": "2121212",
    "ombNumber": "1212-1212",
    "chapters": [
      {
        "id": 158253,
        "chapterTitle": "First Step",
        "type": "digital_form_name_and_date_of_bi",
        "pageTitle": "Name and Date of Birth",
        "additionalFields": {
          "includeDateOfBirth": true
        }
      },
      {
        "id": 158254,
        "chapterTitle": "Second Step",
        "type": "digital_form_name_and_date_of_bi",
        "pageTitle": "Name and Date of Birth",
        "additionalFields": {
          "includeDateOfBirth": false
        }
      }
    ]
  }
]

Related issue(s)

Testing done

  • Added unit tests for the Digital Form post processor
  • Updated unit test for the Digital Form query object
  • Pulled Drupal data from the integration branch deployment to get the before & after outputs above.

What areas of the site does it impact?

Only affects the output of digital-forms.json, which is not currently generated in production.

Acceptance criteria

Quality Assurance & Testing

  • I fixed|updated|added unit tests and integration tests for each feature (if applicable).
  • No sensitive information (i.e. PII/credentials/internal URLs/etc.) is captured in logging, hardcoded, or specs
  • Linting warnings have been addressed
  • Documentation has been updated (link to documentation *if necessary)
  • Screenshot of the developed feature is added
  • Accessibility testing has been performed

Error Handling

  • Browser console contains no warnings or errors.
  • Events are being sent to the appropriate logging solution
  • Feature/bug has a monitor built into Datadog or Grafana (if applicable)

Authentication

  • Did you login to a local build and verify all authenticated routes work as expected with a test user

Requested Feedback

Is this the format desired for the normalization layer? Can you foresee any situations where this format will cause issues?

@va-vfs-bot va-vfs-bot requested a review from a team July 31, 2024 19:37
Copy link
Collaborator

@va-vfs-bot va-vfs-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint is disabled

vets-website uses ESLint to help enforce code quality. In most situations we would like ESLint to remain enabled.

What you can do

See if the code can be refactored to avoid disabling ESLint, or wait for a VSP review.

@@ -0,0 +1,15 @@
/* eslint-disable @department-of-veterans-affairs/axe-check-required */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

@@ -0,0 +1,21 @@
/* eslint-disable @department-of-veterans-affairs/axe-check-required */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

@@ -0,0 +1,14 @@
/* eslint-disable @department-of-veterans-affairs/axe-check-required */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

@@ -0,0 +1,22 @@
/* eslint-disable @department-of-veterans-affairs/axe-check-required */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

@@ -0,0 +1,105 @@
/* eslint-disable @department-of-veterans-affairs/axe-check-required */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

@va-vfs-bot va-vfs-bot temporarily deployed to master/main/88634-create-a-normalization-layer-for-kiss-data July 31, 2024 20:06 Inactive
@va-vfs-bot va-vfs-bot temporarily deployed to master/main/88634-create-a-normalization-layer-for-kiss-data July 31, 2024 20:48 Inactive
@derekhouck derekhouck requested a review from ryguyk July 31, 2024 20:49
@derekhouck derekhouck marked this pull request as ready for review July 31, 2024 20:49
@derekhouck derekhouck requested review from a team as code owners July 31, 2024 20:49
@ryguyk ryguyk changed the base branch from main to integration-form-engine-poc August 2, 2024 18:48
Copy link
Contributor

@ryguyk ryguyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's quickly chat about this, but it looks great!

id: parseInt(entity.entityId, 10),
chapterTitle: entity.fieldTitle,
type: entity.type.entity.entityId,
pageTitle: entity.type.entity.entityLabel,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just acknowledging here that this structure assumes the one-page-per-chapter model, which I'm not sure is going to stand the test of time. For now, though, this is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. That's a bridge we'll cross when we get there.


const normalizeForm = form => {
return {
id: form.nid,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LET'S DISCUSS

I think we need to align on what a form id is. I do think we want to capture the node/entity id somewhere, so this looks good, but my model up to this point (when working on parsing the form id from the url) has been that the form number would have to be available somewhere. I think we might want to add something like:

{
   ...
   formId: form.fieldVaFormNumber,
   ...
}

We might even need to format it in some way (strip spaces, convert to lowercase, etc.):

{
   ...
   formId: formatFormNumber(form.fieldVaFormNumber),
   ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our discussion, formId has been added, id has been renamed to cmsId, and subTitle has been removed.

};
};

const normalizeForms = forms => forms.map(form => normalizeForm(form));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above - this can just be forms.map(normalizeForm). Again, this might be a personal preference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

};

const normalizeChapters = chapters =>
chapters.map(chapter => normalizeChapter(chapter));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be chapters.map(normalizeChapter), though that's a matter of preference I suppose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


return additionalFields;
};
const extractForms = resultObject => resultObject.data.nodeQuery.entities;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if resultObject isn't shaped properly? I think we might want to be defensive here with some optional chaining and appropriate checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and tests added for this scenario.

@va-vfs-bot va-vfs-bot temporarily deployed to master/main/88634-create-a-normalization-layer-for-kiss-data August 7, 2024 15:43 Inactive
Copy link
Contributor

@ryguyk ryguyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very, very nice. Some very robust application code.

I added some commentary around one of the tests, but it's not something I consider blocking approval. I'll give you some time if you feel compelled to change anything or tell me I'm crazy, otherwise I'll get this merged.

}
};

const postProcessDigitalForm = (queryResult, logger = logDrupal) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the dependency injection 😍

let processedResult;

beforeEach(() => {
sinon.stub(drupalUtils, 'logDrupal');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONSIDER

It feels like this should be a spy rather than a stub.

Perhaps more pertinently, though, it feels like we shouldn't need to spy on the specific implementation (drupalUtils.logDrupal), but rather take advantage of the dependency injection you've beautifully included in your application code to pass a dummy logger (see line 177).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

};
processedResult = postProcessDigitalForm(
queryResult,
drupalUtils.logDrupal,
Copy link
Contributor

@ryguyk ryguyk Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONSIDER

Connected to comment above (line 111). Do we need to import an actual implementation and pass it in here? It feels like we would want one of two things here:

  1. Do not pass a logger in here and test that the default logger is called. So, spy on drupalUtils.logDrupal but don't pass it in explicitly.
  2. (Probably more valuable) Utilize the dependency injection, which allows us to better isolate the function in question, and pass a dummy logger.

It's arguable we'd want to do both of these, but, in that case, we would need two separate calls to postProcessDigitalForm. I personally find more value in the second case, but there's always an "it depends".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spying on drupalUtils.logDrupal didn't work for me. Not sure why. But I agree, now that we have the dependency injection, we don't actually need to spy on logDrupal and can just pass in a sinon spy/stub to see that it is being called. I'll update the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@ryguyk ryguyk merged commit 870ea49 into integration-form-engine-poc Aug 10, 2024
20 checks passed
@ryguyk ryguyk deleted the 88634-create-a-normalization-layer-for-kiss-data branch August 10, 2024 12:49
@ryguyk ryguyk mentioned this pull request Aug 10, 2024
19 tasks
ryguyk pushed a commit that referenced this pull request Aug 26, 2024
* Add normalizeForms step to postProcessDigitalForm

* Add subtitle to normalized form

* Add OMB Number to normalized form

* Normalize chapters for each form

* Add additional fields for Name and Date of Birth step

* Fix import spec

* Remove redundant JSON parsing

* Remove JSON conversion from returned value

* Add formID and rename id to cmsId

* Remove subTitle

* Refactor normalizeForms method

* Refactor normalizeChapters

* Defend against malformed query responses

* Remove unused import
ryguyk pushed a commit that referenced this pull request Sep 11, 2024
* Add normalizeForms step to postProcessDigitalForm

* Add subtitle to normalized form

* Add OMB Number to normalized form

* Normalize chapters for each form

* Add additional fields for Name and Date of Birth step

* Fix import spec

* Remove redundant JSON parsing

* Remove JSON conversion from returned value

* Add formID and rename id to cmsId

* Remove subTitle

* Refactor normalizeForms method

* Refactor normalizeChapters

* Defend against malformed query responses

* Remove unused import
ryguyk pushed a commit that referenced this pull request Sep 12, 2024
* Add normalizeForms step to postProcessDigitalForm

* Add subtitle to normalized form

* Add OMB Number to normalized form

* Normalize chapters for each form

* Add additional fields for Name and Date of Birth step

* Fix import spec

* Remove redundant JSON parsing

* Remove JSON conversion from returned value

* Add formID and rename id to cmsId

* Remove subTitle

* Refactor normalizeForms method

* Refactor normalizeChapters

* Defend against malformed query responses

* Remove unused import
ryguyk pushed a commit that referenced this pull request Sep 18, 2024
* Add normalizeForms step to postProcessDigitalForm

* Add subtitle to normalized form

* Add OMB Number to normalized form

* Normalize chapters for each form

* Add additional fields for Name and Date of Birth step

* Fix import spec

* Remove redundant JSON parsing

* Remove JSON conversion from returned value

* Add formID and rename id to cmsId

* Remove subTitle

* Refactor normalizeForms method

* Refactor normalizeChapters

* Defend against malformed query responses

* Remove unused import
ryguyk added a commit that referenced this pull request Sep 24, 2024
* VATEAM-87714: Add KISS configuration for Digital Forms (#2213)

* Create postProcessDigitalForm function

* Add nameAndDateOfBirth fragment

* Create digitalForm GraphQL fragment

* Import nameAndDateOfBirth into digitalForm fragment

* Create digitalForm query object

* Import digitalForm fragment into query object

* Export postProcessDigitalForm as postProcess

* Add Digital Forms to DATA_FILES array

* Fix module imports

* Fix typo

* Adds blank line for consistent spacing.

---------

Co-authored-by: Ryan Koch <ryan.koch.0213@gmail.com>

* VATEAM-88634: Create a normalization layer for Digital Forms (#2215)

* Add normalizeForms step to postProcessDigitalForm

* Add subtitle to normalized form

* Add OMB Number to normalized form

* Normalize chapters for each form

* Add additional fields for Name and Date of Birth step

* Fix import spec

* Remove redundant JSON parsing

* Remove JSON conversion from returned value

* Add formID and rename id to cmsId

* Remove subTitle

* Refactor normalizeForms method

* Refactor normalizeChapters

* Defend against malformed query responses

* Remove unused import

---------

Co-authored-by: Derek Houck <derek@derekhouck.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants